Error codes
Every error string the platform API, the agent-login flow, the OAuth decision endpoint, and the sherbase Data API gateway can return, by HTTP status and route.
Error codes
Most JSON error bodies carry an error field with one of the strings
below. This table covers the platform API (/api/{platform,sherpage, sherserve,sherlink,sherbase,agent-login,oauth,mcp}/...) and the
sherbase Data API gateway (/db/<slug>/...). See
API reference for the shared 401/403 shapes and the
WWW-Authenticate challenge every route uses.
One route doesn't yet fit this table: POST /api/platform/v1/keys
(minting a key) puts a human-readable message directly in error for
name/apps validation failures, rather than one of the stable codes
below — for example "name must be between 1 and 60 characters" or
"apps must be a non-empty array of page, serve, link, base". Match on
HTTP status (400) and read error as prose here, not as a code.
Stable codes for this route's validation failures are planned.
| Code | HTTP | Where it occurs | What to do |
|---|---|---|---|
invalid_api_key |
401 | Any /v1/... route — missing, unrecognized, or revoked credential |
Send a valid shb_... key or sherlock bearer token in Authorization: Bearer. |
invalid_token |
401 | Any /v1/... route — a JWT-shaped credential that failed verification |
Check error_description (token expired, unknown signing key, token issuer or audience mismatch, not an OAuth-obtained token, unknown or deleted client, client registry unavailable, malformed bearer token) and re-authenticate accordingly. |
insufficient_scope |
403 | Any route — credential is valid but lacks the tier, apps scope, or project access the route requires |
Mint or use a key/client with the right scope, or get a project grant — see keys. |
not_activated |
403 | Any /v1/... route, the hosted MCP, and POST /api/account/keys — the credential is valid but the account has not activated |
Activate at dash.shebang.pro, then retry. See activation. |
captcha_failed |
400 | POST /api/account/activate — the Turnstile token was missing, expired, or rejected |
Reload the page and submit again; each token is single-use. |
rate_limited |
429 | POST /api/account/activate — more than five attempts in a rolling 15 minutes |
Wait 15 minutes. |
invalid_phone |
400 | POST /api/account/activate — the number did not parse |
Enter a valid number; Singapore numbers may be typed with or without +65. |
not_eligible |
400 | POST /api/account/activate — the number is not on the invitation list, or is already bound to another account |
Check with Braven which number your invitation used. |
already_activated |
409 | POST /api/account/activate — the account already has a number bound |
Nothing to do. |
invalid_json |
400 | Most POST/PATCH routes across every service — request body isn't valid JSON |
Send a valid JSON body. |
invalid_body |
400 | Platform routes (email, keys, projects, oauth-clients), POST /api/account/activate — body is JSON but missing/wrong-typed required fields |
Match the documented request-body shape for that route. |
invalid_project |
400 | POST/PATCH /api/platform/v1/projects... — a project field fails validation (missing string field, bad color, bad oauth_client_id/status) |
Use the curated color palette and the documented field types. |
invalid_slug |
400 | POST/PATCH /api/platform/v1/projects..., POST /api/sherbase/v1/databases — slug fails the format check |
Use 3–30 lowercase letters/numbers/underscores. |
slug_taken |
409 | POST/PATCH /api/platform/v1/projects... — slug already used by another project |
Pick a different slug. |
slug_conflict |
409 | POST /api/sherpage/v1/pages, POST /api/sherserve/v1/objects — slug already used by another page/object |
Pick a different slug. |
alias_conflict |
409 | PATCH/DELETE on a sherpage page, sherserve object, or sherlink link — the requested short-link alias is taken |
Pick a different alias. |
password_required |
400 | Setting access: "password" on a page, object, or link with no password given |
Supply a password. |
public_id_exhausted |
500 | POST /api/sherpage/v1/pages, POST /api/sherserve/v1/objects — the random public-id space couldn't find a free id |
Retry the call. |
profile_handle_missing |
500 | Any sherpage/sherserve route that needs the caller's profile handle and finds none | Server-side data issue — retry, and report if it persists. |
storage_cleanup_failed |
500 | DELETE on a sherpage page, sherserve object, or sherlink link — the record was removed but its stored files weren't |
The resource is gone from listings; retry isn't meaningful — report if seen. |
link_cleanup_failed |
500 | DELETE on a sherpage page, sherserve object, or sherlink link — the record was removed but its short link wasn't |
Same as storage_cleanup_failed — report if seen. |
invalid_multipart_body |
400 | POST /api/sherserve/v1/objects — malformed multipart upload |
Fix the multipart body (a valid file part with a filename). |
invalid_resource_type |
400 | POST /api/platform/v1/projects/{id}/assign and /unassign |
Name one of the documented resource types. |
client_already_attached |
409 | PATCH /api/platform/v1/projects/{id} — the named OAuth client already has a home project |
Detach it from its current project first, or pick a different client. |
project_not_empty |
409 | DELETE /api/platform/v1/projects/{id} — the project still holds resources |
Move or delete its resources first. |
last_project |
409 | DELETE /api/platform/v1/projects/{id} — this is the account's only remaining project |
An account must keep at least one project. |
cannot_revoke_current_key |
400 | DELETE /api/platform/v1/keys/{id} — the target is the key authenticating this call |
Revoke it from a different key, or from the dashboard's account page. |
forbidden |
403 | /api/platform/v1/oauth-clients — caller isn't a master key |
Use a master key. |
invalid_email_input |
400 | POST /api/platform/v1/email — bad recipient, subject, or body |
Fix the email fields. |
email_quota_exceeded |
429 | POST /api/platform/v1/email — over 10 sends/account/24h |
Wait until the returned retry_after_hours elapses — see limits. |
send_failed |
502 | POST /api/platform/v1/email — the upstream send provider rejected it |
Check the error's message; retry later. |
data_api_disabled |
409 | POST /api/sherbase/v1/databases/{slug}/reload — no gateway instance registered for this slug |
Enable the Data API on this database first. |
data_api_not_enabled |
400 | POST /api/sherbase/v1/databases/{slug}/secret — Data API was never enabled |
Call the enable route first. |
not_found |
404 | Any {slug}/{id} route — no matching resource for this credential |
Check the identifier and that this credential can reach it. |
mfa_required |
403 | POST /api/oauth/decision, POST /api/agent-login/decision — second factor not yet satisfied this session |
Complete the account's second factor, then retry. |
Device login (/api/agent-login/...), RFC 8628 shapes
These use error the same way but follow RFC 8628's device-flow error
set. See device login.
| Code | HTTP | Where it occurs | What to do |
|---|---|---|---|
invalid_request |
400 | device_authorization, token — malformed body or missing required field |
Fix the request body. |
rate_limited |
429 | device_authorization, token — too many requests from one client IP |
Back off and retry. |
unsupported_grant_type |
400 | token — grant_type isn't the device-code grant |
Use urn:ietf:params:oauth:grant-type:device_code. |
authorization_pending |
400 | token — the user hasn't approved yet |
Keep polling at the returned interval. |
slow_down |
400 | token — polling faster than the given interval |
Poll at the larger interval this response returns. |
access_denied |
400 | token — the user denied the request |
Start the device flow again. |
expired_token |
400 | token — the device code's 10-minute lifetime elapsed |
Start the device flow again. |
invalid_grant |
400 | token — the device code is unknown or already claimed |
Start the device flow again. |
Uncaught server errors
| Code | HTTP | Where it occurs | What to do |
|---|---|---|---|
internal_error |
500 | Any route's catch-all handler for an unexpected server-side failure | Retry; report if it persists. |
The sherbase Data API gateway (/db/<slug>/...)
The gateway answers every error Supabase-shaped ({message, code, hint, details}), with code set to the HTTP status as a string rather than a
named error like the routes above:
| HTTP | Message | What it means |
|---|---|---|
| 401 | No API key found in request / Invalid API key |
Missing or unrecognized apikey/Bearer credential for this database. |
| 404 | database not found / Not found |
Unknown slug, or a path that isn't /rest/v1/* or /auth/v1/*. |
| 503 | database starting |
The database's PostgREST instance is still coming up — retry shortly. |
| 502 | upstream unavailable |
PostgREST or sherlock didn't respond — retry. |
| 500 | internal error |
Unexpected gateway failure — report if it persists. |
Next
Limits — the numeric caps behind email_quota_exceeded
and a few of the errors above.