Known compatibility issues
Places where Shelby's S3 API responds differently from Amazon S3, with a workaround for each.
Places where Shelby's S3 API responds differently from Amazon S3, with a workaround for each. None of these affect the durability of stored data. We remove entries as fixes ship.
If-Match on PUT isn't checked yet
A PUT with If-Match overwrites the object even when the ETag does not match. If you use If-Match for optimistic concurrency, two writers can overwrite each other without either seeing an error.
Workaround: Do not rely on If-Match for writes yet. Use If-None-Match: * for create-only writes, which is enforced, or coordinate writers in your application.
Some bucket requests from the AWS SDK for JavaScript aren't recognised
The AWS SDK for JavaScript sends some bucket-level requests with a slash after the bucket name, for example /<bucket>/?delete. Shelby doesn't route that form yet. DeleteObjects returns a plain-text 404 and deletes nothing, and GetBucketLocation, GetBucketVersioning, GetBucketAcl and ListMultipartUploads return the bucket's object listing, which the SDK reads as an empty answer. MinIO mc runs into the same thing when it looks up the bucket's region.
Workaround: In JavaScript, delete keys one at a time with DeleteObjectCommand, set region: "default" in the client rather than looking it up, and abort the multipart uploads you start rather than relying on ListMultipartUploads to find them. For mc, set MC_REGION=default.
New objects can take a few seconds to appear in listings
GetObject and HeadObject see a new or overwritten object as soon as the write returns. ListObjects can take a few seconds longer to include a new object, so a listing taken straight after an upload may not show it yet.
Workaround: If you list right after writing, retry until the key appears, or keep track of the keys you wrote. To check that one object exists, use HeadObject.
Reads that overlap an overwrite can briefly fail
While a PUT is overwriting an object, a GET of the same key can fail with 500 or 503 instead of returning the old or the new version. On S3, a read during an overwrite always gets one version or the other.
Workaround: Retry failed reads with backoff. The AWS SDKs and the AWS CLI retry 500 and 503 by default. If readers use a key while you replace it, write each new version to a new key instead, for example model/v12/weights.bin.
Authentication errors are plain text, not S3 XML
Requests with a wrong key, a missing signature or a skewed clock are rejected with 401 and a short plain-text message, before they reach the S3 layer. SDKs report no S3 error code, and the AWS SDK for JavaScript reports an XML parse error instead.
Workaround: Treat any 401 as a credential or clock problem. See Troubleshooting.
JavaScript SDK presigned links need a client setting
By default, the AWS SDK for JavaScript adds x-amz-checksum-mode=ENABLED to presigned GetObject URLs. Shelby refuses those links with 501 NotImplemented.
Workaround: Set responseChecksumValidation: "WHEN_REQUIRED" on the S3 client you sign with, as shelby-s3.ts in the JavaScript quickstart does. Shelby returns no response checksums, so no check is lost. See Presigned URLs.
max-keys above 1,000 returns an error instead of being capped
S3 clamps max-keys to 1,000. Shelby returns 400 InvalidArgument instead, so a paginator configured with a larger page size fails.
Workaround: Use a page size of 1,000 or less.
Some bucket-configuration calls return 404 or 200 instead of 501
Shelby doesn't implement bucket configuration. Most of these calls return 501 NotImplemented, but some currently return 404 with no S3 error code, or 200 with the object listing (for example GET ?versions and GET ?website). A tool can read that 200 as an empty configuration.
Workaround: Treat bucket configuration as unavailable. None of these settings exist on Shelby.
CompleteMultipartUpload returns an internal Location
The Location element in the CompleteMultipartUpload response names an internal address, not the public endpoint.
Workaround: Ignore Location. Address the object by bucket and key, as SDKs do by default.
Last verified against the hosted endpoint on 2026-11-06.