@swamp/s3-datastore: first-attempt 403 masked as "UnknownError" from AWS SDK deserializer
Opened by stack72 · 4/20/2026
Summary
When the S3 client built by @swamp/s3-datastore hits an HTTP 403 (e.g. InvalidAccessKeyId from DigitalOcean Spaces), the first call through the extension surfaces the error as a generic UnknownError originating from the bundled AWS SDK's XML protocol deserializer. On a subsequent attempt (a separate swamp invocation), the same underlying auth failure surfaces correctly as InvalidAccessKeyId with full $metadata. The first-attempt masking hides the real cause and wastes debugging time.
Stack trace (first attempt)
Observed during swamp model create @swamp/issue-lifecycle issue-1 against a DO Spaces bucket:
{
"error": "UnknownError",
"stack": " at ProtocolLib.getErrorSchemaOrThrowBaseException (file:///.../datastore-bundles/7c811b12/s3.js:13151:63)
at AwsRestXmlProtocol3.handleError (file:///.../datastore-bundles/7c811b12/s3.js:14851:65)
at AwsRestXmlProtocol3.deserializeResponse (file:///.../datastore-bundles/7c811b12/s3.js:6251:22)
..."
}Stack trace (second attempt, same credentials)
Same command, seconds later:
"$fault": "client",
"$metadata": {
"httpStatusCode": 403,
"requestId": "tx0000083c5e12c596eed6c-0069e66dc8-b04dd851-sfo3a",
"attempts": 1
},
"Code": "InvalidAccessKeyId"Same credentials, same bucket, same region — but only the second surfaces an actionable error.
Likely cause
The bundled AWS SDK's XML error deserializer rejects the 403 response body on first contact — most likely because DigitalOcean Spaces returns a non-XML or subtly-malformed error body on some responses (for InvalidAccessKeyId before signature validation completes, some S3-compatible providers return a plain-text or HTML body that fails the SDK's schema assertion). The second attempt presumably hits a different code path (retry, warmed-up client, endpoint re-negotiation) that returns a well-formed XML fault.
Either way, the user-facing outcome is a bare UnknownError stack with no hint that the failure is authentication.
Expected
The extension should wrap S3 operations and catch SDK errors that surface without a parseable body, then surface them with at least:
- HTTP status code (from
$metadata.httpStatusCodewhen present on the raw error, or the response itself) - A short body preview (first N bytes) if the body isn't parseable
- An auth-specific hint when the status is 401/403, naming the credential source (AWS profile, env var
AWS_ACCESS_KEY_ID, IMDS, etc.) so users know where to look
This is consistent with how other swamp extensions surface provider-specific auth failures.
Scope
Fix belongs in the @swamp/s3-datastore extension's S3 call wrapping — wherever the extension invokes the bundled SDK (pullChanged, pushChanged, head/list/put/get paths). Catch Error from the SDK, inspect $metadata / Code / httpStatusCode, and surface a structured error that core swamp can display usefully. No change required in the bundled AWS SDK itself.
Environment
- swamp: 20260206.200442.0-sha
- Platform: macOS (darwin 25.3.0)
- Provider: DigitalOcean Spaces (sfo3 region)
- Datastore config:
@swamp/s3-datastorewithendpoint: https://sfo3.digitaloceanspaces.com - Credentials: AWS profile (
AWS_PROFILE=<name>)
Automoved by swampadmin from https://github.com/systeminit/swamp-extensions/issues/74
Closed
No activity in this phase yet.
stack72 commented 4/20/2026, 9:27:20 PM
Closing this as https://github.com/systeminit/swamp-extensions/pull/76
Sign in to post a ripple.