Skip to content

fix(cli): jwt errors#5862

Open
7ttp wants to merge 2 commits into
supabase:developfrom
7ttp:fix/cli-side-err
Open

fix(cli): jwt errors#5862
7ttp wants to merge 2 commits into
supabase:developfrom
7ttp:fix/cli-side-err

Conversation

@7ttp

@7ttp 7ttp commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

@7ttp 7ttp requested a review from a team as a code owner July 11, 2026 14:33
@github-actions

Copy link
Copy Markdown
Contributor

👋 Thanks! The linked issue hasn't been marked open-for-contribution yet, so this pull request is being closed automatically.

A maintainer adds that label once an issue is triaged and ready to be worked on. Please wait for the label before opening a pull request.
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions github-actions Bot closed this Jul 11, 2026
@7ttp 7ttp reopened this Jul 11, 2026
@7ttp 7ttp self-assigned this Jul 11, 2026
@7ttp

7ttp commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 3da2ad9d1c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@7ttp

7ttp commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

👋 Thanks! The linked issue hasn't been marked open-for-contribution yet, so this pull request is being closed automatically.

A maintainer adds that label once an issue is triaged and ready to be worked on. Please wait for the label before opening a pull request. See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

imo this shouldn't really happen for the members ? 😅

thoughts @avallete @jgoux ?

@avallete avallete added open-for-contribution Open for contribution allow any external user to submit a PR fixing this issue and removed open-for-contribution Open for contribution allow any external user to submit a PR fixing this issue labels Jul 12, 2026
@avallete

Copy link
Copy Markdown
Member

👋 Thanks! The linked issue hasn't been marked open-for-contribution yet, so this pull request is being closed automatically.
A maintainer adds that label once an issue is triaged and ready to be worked on. Please wait for the label before opening a pull request. See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

imo this shouldn't really happen for the members ? 😅

thoughts @avallete @jgoux ?

It shouldn't, sadly if your affiliation is "private" on https://github.com/orgs/supabase/people
Then, since the GH token for the check is a public one, that won't be detected by the action. Then, the fallback is to look for active repo permissions (some admin are private members) but that doesn't apply in your case either 😅

If that's not a bother, could you try to your supabase membership to "public" ? This should allows your PR's to pass the gate without issues.

@avallete avallete left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this might actually be a BC need to double check.

@avallete

avallete commented Jul 13, 2026

Copy link
Copy Markdown
Member

Alright, tell me if I'm mistaken @kallebysantos @7ttp but the Hosted platform does not uses { error } at all does they ?

Looking at the code, I think this is the wrong fix, and we actually need a contract between edge-functions/stdio/cli for error codes, they seems to solely rely on { code, message } shape. This seems to introduce one more schema rather than align with platform:

   Runtime                     JWT error body
  ━━━━━━━━━━━━━━━━━━━━━━━━━━  ━━━━━━━━━━━━━━━━━━━
   Hosted platform             { code, message }
  ──────────────────────────  ───────────────────
   Stable local CLI today      { msg }
  ──────────────────────────  ───────────────────
   PR #5862                    { error }
  ──────────────────────────  ───────────────────
   Next/alpha local stack      { msg }
  ──────────────────────────  ───────────────────
   Self-hosted Docker today    { msg }

The official docs shows "error, message" contract: https://github.com/supabase/supabase/blob/master/apps/docs/content/troubleshooting/edge-function-401-error-response.mdx

And it look like the only place where we rely on error in studio is there: https://github.com/supabase/supabase/blob/master/apps/studio/pages/api/edge-functions/test.ts#L95-L100

I think we should standardize toward { code, message } instead of adding another contract ?
I think the root cause of the issue is in studio:

flowchart LR
    UI["Studio Test UI"]
    SP["Studio test proxy<br/>currently assumes body.error"]
    GW["Gateway<br/>Kong / Envoy / hosted router"]
    JW["JWT admission layer<br/>hosted ingress / local main worker"]
    UF["User function"]

    UI --> SP --> GW --> JW --> UF

    GW -. "503: {message} or text" .-> SP
    JW -. "401: hosted {code,message}<br/>local legacy {msg}" .-> SP
    UF -. "arbitrary status/body/headers" .-> SP
Loading

The bug I think is https://github.com/supabase/supabase/blob/master/apps/studio/pages/api/edge-functions/test.ts#L92-L107 :

  1. It receives a legitimate non-2xx response.
  2. It discards the body and headers.
  3. It tries only errorBody.error.
  4. Everything else becomes “Edge function returned an error.”

That assumption is impossible to satisfy universally:

I think a minimal cleaner fix:

I would make the Studio test endpoint a transparent response adapter.

A completed upstream request even if it returns 401, 404, or 503 is a successful execution of the test operation. It should return:

{
  status: upstream.status,
  headers: upstreamHeaders,
  body: upstreamBody
}

The Studio proxy itself should return HTTP 200 for that envelope. Reserve proxy-level 4xx/5xx for cases where Studio could not execute the test at all: invalid URL, network failure, malformed request, etc.

Concretely, in supabase/apps/studio/pages/api/edge-functions/test.ts:

  • always use await response.text() do not parse and reserialize based on Content-Type.
  • always collect upstream headers.
  • remove the special !response.ok schema extraction.
  • return the same {status, headers, body} envelope for every upstream status.

This fits the existing architecture unusually well:

#47855 (https://github.com/supabase/supabase/pull/47855)’s tolerant extractor is an acceptable hotfix, but it remains schema guessing and still loses response headers and structured bodies. Transparent relay is cleaner.

Tests should cover hosted {code,message}, Kong {message}, legacy {msg}, nested JSON, arbitrary JSON, plain text, invalid JSON, headers, and a genuine fetch exception.

Where JWT mirror code should live

JWT errors belong at the layer that performs JWT admission not in Studio and not in the generic Edge Runtime engine.

The canonical contract should be the hosted ingress contract:

HTTP/1.1 401 Unauthorized
Content-Type: application/json
sb-error-code: UNAUTHORIZED_NO_AUTH_HEADER

{
  "code": "UNAUTHORIZED_NO_AUTH_HEADER",
  "message": "Missing authorization header"
}

Canonical codes:

  • UNAUTHORIZED_NO_AUTH_HEADER
  • UNAUTHORIZED_INVALID_JWT_FORMAT
  • UNAUTHORIZED_LEGACY_JWT
  • UNAUTHORIZED_ASYMMETRIC_JWT
  • UNAUTHORIZED_UNSUPPORTED_TOKEN_ALGORITHM

The mirrors should live beside JWT verification in each independently shipped bootstrap:

  • Hosted authority: edge-functions-ingress/src/main/errors.ts, jwt.ts, and utils.ts.
  • Self-hosted Docker: supabase/docker/volumes/functions/main/index.ts.
  • Stable CLI runtime: cli/apps/cli/src/shared/functions/serve.main.ts.
  • Next/alpha CLI runtime: cli/packages/stack/src/services/edge-runtime-main.ts.

Each bootstrap should have a small local JwtFailure mapping and one unauthorized(failure) response helper.

Backward-compatible migration

Local {msg} has shipped since 2023, so replace it additively:

{
  "code": "UNAUTHORIZED_INVALID_JWT_FORMAT",
  "message": "Invalid JWT format",
  "msg": "Invalid JWT format"
}

Also emit sb-error-code. Treat msg as deprecated and remove it only in an explicit major-version change or keep the tiny alias indefinitely.

Recommended sequence:

  1. Fix Studio to preserve arbitrary upstream responses.
  2. Add canonical {code,message} and sb-error-code to all JWT admission bootstraps.
  3. Keep legacy msg during migration.
  4. Add identical response-contract tests in self-hosted, stable CLI, and next CLI.
  5. Re-scope fix(cli): jwt errors #5862 away from msg → error, to {code,message} it currently also misses the next CLI path.

The important boundary is to standardize all Supabase-owned JWT errors, preserve all other HTTP responses transparently.

Trying to standardize gateway failures and user-function responses would require rewriting responses that Supabase does not own.

cc @SaxonF @joshenlim since it seems you authored: https://github.com/supabase/supabase/blame/master/apps/studio/pages/api/edge-functions/test.ts#L92-L107 would you give it a look, tell me if you think I'm off ?

@kallebysantos

kallebysantos commented Jul 13, 2026

Copy link
Copy Markdown
Member

You're right... I missed it, and I agree that we should use a single pattern across all environments.
As addition to @avallete comment: https://supabase.com/docs/guides/functions/error-codes

We've been refactoring Platform error codes, and yes! we're now using {code, message} object, example:

export enum AuthErrors {
  UnsupportedTokenAlgorithm = 'UNAUTHORIZED_UNSUPPORTED_TOKEN_ALGORITHM',
  MissingAuthHeader = 'UNAUTHORIZED_NO_AUTH_HEADER',
  // ....
}
// in case JWT validation fails:
const error = {
    code: AuthErrors.UnsupportedTokenAlgorithm,
    message: `Unsupported JWT algorithm ${jwt.alg}`
}

return Response.json(error, { 
  status:  Status.Unauthorized,
  headers: {
    'sb-error-code': AuthErrors.UnsupportedTokenAlgorithm
  }
})

@7ttp 7ttp force-pushed the fix/cli-side-err branch 2 times, most recently from 2af6b28 to fd9e5a1 Compare July 13, 2026 15:46
@7ttp 7ttp marked this pull request as draft July 13, 2026 15:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd9e5a1a7d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/cli/src/shared/functions/serve.main.ts Outdated
Comment thread apps/cli/src/shared/functions/serve.main.ts Outdated
@7ttp 7ttp force-pushed the fix/cli-side-err branch from fd9e5a1 to 921984a Compare July 13, 2026 17:26
@7ttp 7ttp marked this pull request as ready for review July 13, 2026 17:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 921984a8e4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/cli/src/shared/functions/serve.main.ts
@7ttp

7ttp commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7cfdaeed5a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/cli/src/shared/functions/serve.main.ts
@7ttp

7ttp commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

mb! @avallete @kallebysantos

I’ve rescoped this PR to the { code, message } pattern (kept msg for now for BC)

but b4 this will patch the studio test proxy so it preserves the upstream status, headers, and body instead of keying only off errorBody.error (waiting for the team's take on it)

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.

Edge function JWT verification errors return { msg } instead of { error } in serve.main.ts

3 participants