Skip to content

fix(client): give 401, 403 and 409 their own error kinds (GHY-4738) - #70

Merged
dpsutton merged 5 commits into
mainfrom
ghy-4738-show-403-reason
Oct 7, 2026
Merged

dpsutton merged 5 commits into
mainfrom
ghy-4738-show-403-reason

Conversation

@dpsutton

@dpsutton dpsutton commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes GHY-4738: a transform on an existing table fails with "Invalid or unauthorized API key".

Why. mb treated every 401 and 403 as a bad key. Only a 401 is: Metabase didn't accept the credential (a bogus or missing key gets 401). A 403 means Metabase identified the user and refused. A 409 means the request conflicts with existing state; metabase/metabase#83799 makes "A table with that name already exists." a 409.

Fix (1 file). packages/client/src/http/errors.ts: each status gets its own kind; the status decides, and the server's text only makes a message specific.

Status Kind Message
401 auth Invalid or unauthorized API key (host: …). (or the server's JSON message)
403 forbidden (new) the server's reason, else "The request was refused (403): the signed-in user is not allowed to do this."
409 conflict (new) the server's reason, else "The request was refused (409): it conflicts with what already exists."

Fallout. core/auth/verify.ts: mb auth list marks a profile auth-failed only on a 401; a 403 from /api/user/current comes from a proxy and reads as a server problem (stored kinds unchanged). resources/dashboard.ts: an unreadable card is auth or forbidden. index.test.ts: the exhaustive kind switch gains the two kinds. errors.test.ts: per-status kind and message tests. tests/e2e/profiles.e2e.test.ts: the limited-key 403 reads "You don't have permissions to do that.". Client README lists the kinds.

Verified. bun run check passes (1786 tests). Live against Metabase master with the ticket's repro: main says Invalid or unauthorized API key (host: localhost:23000)., this branch says A table with that name already exists. A bogus or missing key still gets 401 there, so the invalid-key e2e expectations hold.

🤖 Generated with Claude Code

…API key

A 403 means Metabase accepted the key and refused the request. Metabase
answers some validation errors that way with a text/plain reason, such as
"A table with that name already exists." for a transform whose target
table exists, and a permission refusal with "You don't have permissions to
do that." Both read as "Invalid or unauthorized API key", which sent people
and agents off debugging authentication. A 403's plain-text reason is now
the message; a 401, whose body is only "Unauthenticated", and a 403 without
one keep the key message.

GHY-4738

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear

linear Bot commented Oct 6, 2026

Copy link
Copy Markdown

GHY-4738

dpsutton and others added 2 commits October 6, 2026 15:45
…usal

A 401 is a credential Metabase did not accept; a 403 is a request it
refused from a user it identified. A 403 without a plain-text reason
still said "Invalid or unauthorized API key", so the right message
depended on Metabase sending a body. It now reads as a refusal either
way, and a reason, when present, makes it specific.

GHY-4738

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
401 stays `auth`: Metabase did not accept the credential, so the message
names the API key. 403 is `forbidden`, a request refused for a user
Metabase identified, and 409 is `conflict`, a request that clashes with
existing state; neither is about the key. Both show the server's reason
and otherwise fall back to a default for their status. The dashboard
card check treats `forbidden` like `auth`: the card is unreadable.

GHY-4738

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dpsutton dpsutton changed the title fix(client): name Metabase's reason for a 403 instead of blaming the API key fix(client): give 401, 403 and 409 their own error kinds (GHY-4738) Oct 6, 2026
dpsutton and others added 2 commits October 6, 2026 17:04
… responder

The 403 default named "the API key's user", which an OAuth login doesn't
have, and both defaults said Metabase refused, though a proxy in front of
it can answer too. They now say the request was refused, with the status.

GHY-4738

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
verify read 401 and 403 from /api/user/current alike as "auth", so
`mb auth list` said to update the token. Metabase serves every identified
user their own record; a 403 there comes from whatever answered instead.
verify now takes the client's own kind: `auth` (401) is a credential
problem, anything else a server one. The stored failure kinds don't
change.

GHY-4738

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dpsutton
dpsutton merged commit 999b96e into main Oct 7, 2026
26 checks passed
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.

2 participants