# Known compatibility issues (/tools/s3-gateway/known-issues)

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.

{/*
  GENERATED FRAGMENT. Do not edit by hand.
  Source: data/s3-compat.json, checked against the beta-harness corpus.
  Regenerate: pnpm generate:s3
  */}

<Callout type="warn" title={"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.
</Callout>

<Callout type="info" title={"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`.
</Callout>

<Callout type="info" title={"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`.
</Callout>

<Callout type="info" title={"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](/tools/s3-gateway/troubleshooting#authentication-errors).
</Callout>

<Callout type="info" title={"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.
</Callout>

<Callout type="info" title={"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.
</Callout>

<Callout type="info" title={"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.
</Callout>

{/*
  GENERATED FRAGMENT. Do not edit by hand.
  Source: data/s3-compat.json, checked against the beta-harness corpus.
  Regenerate: pnpm generate:s3
  */}

*Last verified against the hosted endpoint on 2026-10-27.*
