Skip to content

fix(storage): expose service error code on StorageApiError - #2537

Merged
mandarini merged 3 commits into
supabase:masterfrom
thribhuvan003:fix/storage-error-code
Jul 31, 2026
Merged

fix(storage): expose service error code on StorageApiError#2537
mandarini merged 3 commits into
supabase:masterfrom
thribhuvan003:fix/storage-error-code

Conversation

@thribhuvan003

Copy link
Copy Markdown
Contributor

🔍 Description

What changed?

StorageApiError now carries the service error code (NoSuchKey, AccessDenied, ResourceAlreadyExists, etc.) on a dedicated code property, and includes it in toJSON().

The response body already contains this in err.codehandleError in storage-js/src/lib/common/fetch.ts was only using it as a fallback for statusCode and otherwise dropping it, so it never reached the error object. Now it is passed through to the new field. statusCode is unchanged.

Why was this change needed?

The Storage error-codes docs tell you to branch on codes like AccessDenied / NoSuchKey, but the thrown StorageApiError never exposed them — it only had status and statusCode — so that pattern was impossible to implement without parsing the message string. This closes that gap.

Closes #2536

📸 Screenshots/Examples

const { error } = await supabase.storage.from("bucket").download("missing.png")
if (error && error.name === "StorageApiError" && error.code === "NoSuchKey") {
  // handle the specific case
}

🔄 Breaking changes

  • This PR contains no breaking changes

Additive only — a new optional constructor arg and a new field. code is undefined when the response has no code, and existing status / statusCode behaviour is untouched.

📋 Checklist

  • I have read the Contributing Guidelines
  • My PR title follows the conventional commit format: <type>(<scope>): <description>
  • I have run pnpm nx format to ensure consistent code formatting
  • I have added tests for new functionality (if applicable)
  • I have updated documentation (if applicable)

📝 Additional notes

Added unit tests in both errors.test.ts (the code field + toJSON) and fetch.test.ts (that a code in the response body reaches the error) — they fail without the change and pass with it.

@thribhuvan003
thribhuvan003 requested review from a team as code owners July 19, 2026 13:09
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa7bb058-838c-4079-8f13-92cd076abbbf

📥 Commits

Reviewing files that changed from the base of the PR and between 834f93f and 691cf89.

📒 Files selected for processing (5)
  • packages/core/storage-js/src/lib/common/errors.ts
  • packages/core/storage-js/src/lib/common/fetch.ts
  • packages/core/storage-js/test/common/errors.test.ts
  • packages/core/storage-js/test/common/fetch.test.ts
  • sdk-compliance.yaml
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/core/storage-js/test/common/fetch.test.ts
  • packages/core/storage-js/src/lib/common/fetch.ts
  • packages/core/storage-js/src/lib/common/errors.ts
  • packages/core/storage-js/test/common/errors.test.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Storage errors now expose service-specific error codes, such as NoSuchKey or AccessDenied.
    • Serialized storage errors include the service error code when available.
  • Bug Fixes

    • Improved propagation of service error codes from failed storage requests, making failures easier to diagnose end-to-end.
  • Tests

    • Added coverage confirming error codes are preserved, serialized, and omitted when unavailable.

Walkthrough

StorageApiError now accepts an optional service-specific code, exposes it on the error instance, and includes it in toJSON(). Fetch error handling passes the parsed response body's code into the error constructor. Tests cover provided and omitted codes, serialization, and propagation from a 404 response. The SDK compliance matrix marks storage error codes as implemented.

Assessment against linked issues

Objective Addressed Explanation
Expose documented Storage service error codes on StorageApiError [#2536]

Possibly related PRs

Suggested labels: needs server code


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mandarini mandarini left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @thribhuvan003, thank you so much for contributing to Supabase! 💚

This is exactly the right fix. You landed on the correct field: the Storage service always sends the documented code (NoSuchKey, AccessDenied, and so on) as code in the response body, and the SDK was quietly dropping it because statusCode took precedence. Passing err.code through onto a dedicated code property, and including it in toJSON(), is precisely what we want.

Two small things before we merge, neither blocking:

  1. In the new fetch test the mock body is { code: 'NoSuchKey', message: 'Object not found' } with no statusCode. That happens to exercise the legacy fallback where statusCode also becomes 'NoSuchKey', which is not what a real server sends. The real payload always includes both, like { statusCode: '404', code: 'NoSuchKey', message: '...' }. Could you tweak the fixture to include statusCode so it mirrors reality? The assertion on code stays the same.

  2. Optional, but nice to have: a small integration test that downloads a missing object and asserts error.code, so we lock in that the server really does send what we are reading. Happy to merge without it if you would rather keep the PR tight.

Thank you again for the clean, focused patch and for the tests that actually fail without the change. Contributions like this are what keep the SDK healthy.

@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@supabase/auth-js

npm i https://pkg.pr.new/@supabase/auth-js@2537

@supabase/functions-js

npm i https://pkg.pr.new/@supabase/functions-js@2537

@supabase/postgrest-js

npm i https://pkg.pr.new/@supabase/postgrest-js@2537

@supabase/realtime-js

npm i https://pkg.pr.new/@supabase/realtime-js@2537

@supabase/storage-js

npm i https://pkg.pr.new/@supabase/storage-js@2537

@supabase/supabase-js

npm i https://pkg.pr.new/@supabase/supabase-js@2537

commit: 691cf89

@coveralls

coveralls commented Jul 20, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 81.355% (-0.001%) from 81.356% — thribhuvan003:fix/storage-error-code into supabase:master

@thribhuvan003

Copy link
Copy Markdown
Contributor Author

thanks @mandarini, that means a lot 💚

done on (1) — the fetch fixture now sends { statusCode: '404', code: 'NoSuchKey', message: '...' } like the real server, and asserts both statusCode: '404' and code: 'NoSuchKey' so it no longer leans on the legacy fallback. all unit tests still green.

on (2): i'd like to add it, but i can't run the storage docker stack in my dev env to verify the exact code a real missing-object download returns, and i didn't want to push an integration assertion i haven't actually seen pass. happy to add one if you're fine trusting CI to validate it, or if you can confirm the code a 404 download surfaces — otherwise glad to keep it tight per your offer. either way, thanks for the super clear review.

@mandarini

mandarini commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Really appreciate the follow-up, and you asked exactly the right question in (2): what code does a real 404 download surface? I went into the Storage server to confirm, and the answer turned out to be surprising: none does. That also means the fixture in (1) doesn't quite mirror the real server. Τhe actual 4xx body is { statusCode: '404', error: 'not_found', message: 'Object not found' }, with no code field.

Here's why: the server generates NoSuchKey internally (codes.ts) and even sets it in render() (storage-error.ts) — but every object route serializes its 4xx body through a Fastify response schema, errorSchema, that whitelists only statusCode/error/message. Fastify drops anything else, so code never reaches the client. The server's own tests confirm it: a 404 NoSuchKey asserts an exact body with no code (object.test.ts#L246); a 500 keeps code only because 5xx has no schema to strip it.

Your instinct not to push an integration assertion you hadn't seen pass was correct, it would have failed, and that's what surfaced this. The client patch is the right shape, but it can't deliver until the server includes code in errorSchema. I'll follow up on the server side to get code emitted; once it is, your change is exactly what we pair with it. Genuinely great catch! It spans both repos.

@mandarini mandarini added the needs server code This feature/fix needs changes on the server side, before the client side changes can take effect. label Jul 21, 2026
@mandarini mandarini self-assigned this Jul 21, 2026
@mandarini

Copy link
Copy Markdown
Contributor

@thribhuvan003 good news btw, storage team added it on their timeline! so once they make a PR i will link it here, and once PR is merged and released, we can move on with your PR!

@mandarini mandarini added the do-not-merge Do not merge this PR. label Jul 21, 2026
@thribhuvan003

Copy link
Copy Markdown
Contributor Author

amazing, thank you 🙌 no rush at all on my end — i'll watch for the storage team's PR and align mine once it lands.

@ferhatelmas

Copy link
Copy Markdown
Member

supabase/storage#1256

@mandarini

Copy link
Copy Markdown
Contributor

@thribhuvan003 this is now shipped on the storage server!

@thribhuvan003
thribhuvan003 force-pushed the fix/storage-error-code branch from 1c9c7c0 to 834f93f Compare July 30, 2026 00:44
@thribhuvan003

Copy link
Copy Markdown
Contributor Author

Thanks @mandarini — great news, and thank you for driving the server-side change!

I've rebased onto the latest master, and the client already reads the newly shipped code field, so both sides now line up.

One thing on the SDK Compliance check: it flags StorageApiError.code and the inherited StorageVectorsApiError.code as new public symbols, but I couldn't find an existing Storage error-code capability to register them under. Could you let me know where you'd prefer them mapped? Happy to update the compliance file accordingly.

@mandarini mandarini removed do-not-merge Do not merge this PR. needs server code This feature/fix needs changes on the server side, before the client side changes can take effect. labels Jul 31, 2026
mandarini added a commit to supabase/sdk that referenced this pull request Jul 31, 2026
Registers a new area-level feature for exposing the machine-readable
service error code (e.g. NoSuchKey, AccessDenied) on thrown storage
errors, covering both file and vector bucket operations. Needed to
map StorageApiError.code / StorageVectorsApiError.code being added in
supabase/supabase-js#2537.
mandarini added a commit to supabase/sdk that referenced this pull request Jul 31, 2026
Registers a new area-level feature for exposing the machine-readable
service error code (e.g. NoSuchKey, AccessDenied) on thrown storage
errors, covering both file and vector bucket operations. Needed to
map StorageApiError.code / StorageVectorsApiError.code being added in
supabase/supabase-js#2537.
@mandarini
mandarini force-pushed the fix/storage-error-code branch from 834f93f to 691cf89 Compare July 31, 2026 10:32
@mandarini
mandarini requested review from a team as code owners July 31, 2026 10:32
@mandarini
mandarini self-requested a review July 31, 2026 10:32
@mandarini
mandarini merged commit b109ea0 into supabase:master Jul 31, 2026
29 checks passed
mandarini pushed a commit to supabase/ssr that referenced this pull request Aug 11, 2026
This PR updates `@supabase/supabase-js` to v2.112.3.

**Source**: manual

---

## Release Notes

## v2.112.3

## 2.112.3 (2026-08-11)

### 🩹 Fixes

- **supabase:** add trace context headers to canonical CORS allow-list
([#2603](supabase/supabase-js#2603))
- **supabase:** improve trace propagation sampling and diagnostics
([#2604](supabase/supabase-js#2604))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.112.2

## 2.112.2 (2026-08-06)

### 🩹 Fixes

- **realtime:** prevent duplicate on bindings
([#2594](supabase/supabase-js#2594))
- **realtime:** clear stale join payload on sign-out
([#2597](supabase/supabase-js#2597))

### ❤️ Thank You

- Filipe Cabaço @filipecabaco
- Vaibhav @7ttp
## v2.112.1

## 2.112.1 (2026-08-05)

### 🩹 Fixes

- **auth:** preserve 5xx error message
([#2587](supabase/supabase-js#2587))
- **realtime:** ensure setAuth doesn't disable token refresh
([#2592](supabase/supabase-js#2592))

### ❤️ Thank You

- Eduardo Gurgel
- Vaibhav @7ttp
## v2.112.0

## 2.112.0 (2026-08-03)

### 🚀 Features

- **supabase:** move OpenTelemetry tracing to opt-in /tracing subpath
([#2583](supabase/supabase-js#2583))

### 🩹 Fixes

- **auth:** accept uppercase UUIDs in validateUUID
([#2467](supabase/supabase-js#2467))
- **postgrest:** honour throwOnError when maybeSingle finds multiple
rows ([#2580](supabase/supabase-js#2580))
- **storage:** resolve createSignedUrls return type mismatch
([#2474](supabase/supabase-js#2474))
- **storage:** expose service error code on StorageApiError
([#2537](supabase/supabase-js#2537))
- **supabase:** forward db retry option
([#2571](supabase/supabase-js#2571))

### ❤️ Thank You

- Anubhav Anand @i-anubhav-anand
- Gourab Singha @gourabsingha1
- Juhef @juheff
- Katerina Skroumpelou @mandarini
- Thribhuvan
- Vaibhav @7ttp
- Zuhef Ahmed @Zuhef

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
mandarini pushed a commit to supabase/supabase that referenced this pull request Aug 11, 2026
This PR updates @supabase/*-js libraries to version 2.112.3.

**Source**: manual

**Changes**:
- Updated @supabase/supabase-js to 2.112.3
- Updated @supabase/auth-js to 2.112.3
- Updated @supabase/realtime-js to 2.112.3
- Updated @supabase/postgest-js to 2.112.3
- Refreshed pnpm-lock.yaml

---

## Release Notes

## v2.112.3

## 2.112.3 (2026-08-11)

### 🩹 Fixes

- **supabase:** add trace context headers to canonical CORS allow-list
([#2603](supabase/supabase-js#2603))
- **supabase:** improve trace propagation sampling and diagnostics
([#2604](supabase/supabase-js#2604))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.112.2

## 2.112.2 (2026-08-06)

### 🩹 Fixes

- **realtime:** prevent duplicate on bindings
([#2594](supabase/supabase-js#2594))
- **realtime:** clear stale join payload on sign-out
([#2597](supabase/supabase-js#2597))

### ❤️ Thank You

- Filipe Cabaço @filipecabaco
- Vaibhav @7ttp
## v2.112.1

## 2.112.1 (2026-08-05)

### 🩹 Fixes

- **auth:** preserve 5xx error message
([#2587](supabase/supabase-js#2587))
- **realtime:** ensure setAuth doesn't disable token refresh
([#2592](supabase/supabase-js#2592))

### ❤️ Thank You

- Eduardo Gurgel
- Vaibhav @7ttp
## v2.112.0

## 2.112.0 (2026-08-03)

### 🚀 Features

- **supabase:** move OpenTelemetry tracing to opt-in /tracing subpath
([#2583](supabase/supabase-js#2583))

### 🩹 Fixes

- **auth:** accept uppercase UUIDs in validateUUID
([#2467](supabase/supabase-js#2467))
- **postgrest:** honour throwOnError when maybeSingle finds multiple
rows ([#2580](supabase/supabase-js#2580))
- **storage:** resolve createSignedUrls return type mismatch
([#2474](supabase/supabase-js#2474))
- **storage:** expose service error code on StorageApiError
([#2537](supabase/supabase-js#2537))
- **supabase:** forward db retry option
([#2571](supabase/supabase-js#2571))

### ❤️ Thank You

- Anubhav Anand @i-anubhav-anand
- Gourab Singha @gourabsingha1
- Juhef @juheff
- Katerina Skroumpelou @mandarini
- Thribhuvan
- Vaibhav @7ttp
- Zuhef Ahmed @Zuhef
## v2.111.0

## 2.111.0 (2026-07-28)

### 🚀 Features

- **auth:** store PKCE verifiers in per-flow slots to survive
overlapping flows
([#2569](supabase/supabase-js#2569))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

StorageApiError does not expose documented Storage error codes (error)

5 participants