Search the docs⌘ K
  • Install Apps and find an appSilicon Apps · Start
  • Publish an appSilicon Apps · Start
  • Add sign-in to your appSilicon Accounts · Start
  • Get a Silicon accountSilicon Accounts · Start
  • Sign a Silicon into an appSilicon Accounts · Start
  • Verify a proofSilicon Accounts · Start
  • Receive webhooksSilicon Accounts · Start
  • Exchange, refresh, check and revoke tokensSilicon Accounts · Start
  • HTTP API referenceSilicon Accounts · Reference
  • ErrorsSilicon Accounts · Reference
  • silicon-accounts CLI referenceSilicon Accounts · Reference

Errors

Every error code we return, what caused it and what to do next, across the HTTP API, OAuth, the Rust client, webhooks and the CLI.

ReferenceUpdated MarkdownEdit on GitHub
On this page

When a request fails, we tell you why in the error's code and message, plus a hint and details when there is something to add. The code says what kind of failure it is, the message explains what happened, and the hint suggests what to do next.

Branch on the code in your program, never on the message: we may reword a message, but the code stays. The tables below give the cause of every code and how to recover from it.

Shell
curl -s -X POST "$ACCOUNTS_URL/v1/silicons/login" -H 'Content-Type: application/json' \
  -d '{"id":"si:scout","stk":"stk-000000000000"}'
JSON
{
  "error": {
    "code": "invalid_credentials",
    "message": "Sign-in failed: no Silicon has this si:id, or the STK is wrong. Both cases get this same answer, so ids can't be probed.",
    "hint": "Check the si:id (use the current one; ids can change) and the STK (stk- followed by the hex characters shown once at creation or rotation). 10 wrong STKs in a row lock sign-in for 1 minute. A lost STK can be replaced by the Silicon's custodian (`silicon-accounts silicon rotate-stk`)."
  }
}

The two shapes

Everything except the three OAuth endpoints answers like this:

JSON
{ "error": { "code": "…", "message": "…", "hint": "…", "details": { } } }
  • code: stable, snake_case. Branch on it, never on the message text.
  • message: what went wrong and why, naming the values involved.
  • hint: what to do next (sometimes missing).
  • details: structured extras: fields (422 validation_failed: path → problem), retry_after_seconds (423, 429), suggestions (id_taken), request_id (5xx), and the per-code details listed below.
  • 423 and 429 also set the Retry-After header. 401 responses are Cache-Control: no-store. 5xx bodies never describe our internals.

POST /v1/oauth/token, /v1/oauth/revoke and /v1/oauth/introspect answer with RFC 6749 bodies, {"error": "invalid_grant", "error_description": "…"}, because that's what OAuth libraries expect (OAuth errors).

Always log the X-Request-Id response header with an error. It identifies the request in our logs, and silicon-accounts report and POST /v1/reports take it in the message.

How to react, by status

StatusMeaningWhat to do
400the request is malformedfix the request; retrying it unchanged fails again
401no or bad credentialssign in again, or fix the token or app secret
403authenticated but not alloweda different account or app, or a different route, is needed
404not found (or not visible to you)check the identifier
409conflicts with the current stateread the current state, then decide
410expiredstart that step again
413 / 415body too large / wrong media typesend a smaller or correct body
422well-formed but invalid valuesfix the fields named in the message or details.fields
423locked after too many failureswait Retry-After seconds
429rate limitedwait Retry-After seconds
5xxa fault on our side, or a timeoutretry later with the same Idempotency-Key; report it with the request id if it persists

Request format

CodeStatusCause and fix
invalid_json400the body isn't valid JSON (line and column given)
invalid_content_type400a body was sent without Content-Type: application/json (or, for imports, text/csv)
invalid_body400the body couldn't be read (connection broken mid-upload)
invalid_query400a query parameter is missing, has the wrong type or an unknown value (named)
invalid_path400a path parameter is malformed
invalid_cursor400cursor isn't a next_cursor from this list; pass it unchanged or omit it
invalid_request400the request is missing something it needs (the message names it)
validation_failed422fields are missing, of the wrong type, invalid, or unknown: details.fields maps each path (branding.radius, scopes[3]) to its problem; every problem is reported at once
payload_too_large413the body is over the route's limit (details.limit_bytes: 64 KB by default)
unsupported_media_type415a photo upload's Content-Type isn't PNG, JPEG, WebP or GIF
route_not_found404no endpoint has this path
method_not_allowed405the path exists with other methods (the Allow header lists them)
not_found404a file of the account site doesn't exist

Idempotency

CodeStatusCause and fix
invalid_idempotency_key400Idempotency-Key isn't 1 to 200 visible ASCII characters (no spaces)
idempotency_key_reused409the key was used for a different body on this endpoint; use a new key for a new request
idempotency_in_progress409a request with this key is still running; retry in a few seconds
idempotency_result_unavailable409the stored secret-bearing result can no longer be decrypted, so it isn't run again; check the current state (e.g. list your Silicons) before retrying with a new key
idempotency_key_required400reserved for endpoints that require a key (none currently do)

Authentication and permission

CodeStatusCause and fix
unauthenticated401no credentials; sign in (silicon-accounts login) or send the app's Basic credentials
account_auth_required401app credentials (Basic) were sent to an endpoint that acts for an account
invalid_authorization401the Authorization header is unreadable or uses an unsupported scheme
invalid_token401not an access token, a bad signature, or expired (access tokens last 30 minutes: refresh)
token_wrong_audience401an app's token was used where a first-party (aud = silicon-accounts) token is needed, or a developer-platform token (aud = developer, details.aud) outside the routes it may use (GET /v1/me, GET /v1/session, GET /v1/me/owned-apps and the owner routes under /v1/apps/{app_id}/…); the message names the method and route
identity_token_not_accepted401an identity token (token_use: identity, made for AWS, Google Cloud or Entra) was sent as a bearer token; send the access token
token_revoked401the sign-in behind the token ended (signed out, STK rotated, account deleted, refresh token reuse, a removed key or trust); the message says when and why; sign in again
session_expired401the session cookie was signed out, revoked or expired
account_deleted401 / 403 / 404 / 409the account was deleted: 401 for its own tokens, 403 at Silicon sign-in, 404 at lookups, 409 when it happened during the request
origin_not_allowed403a cookie-authenticated POST/PUT/PATCH/DELETE came without the account site's Origin; use a Bearer token instead of the cookie
carbon_only403a Silicon called an endpoint for Carbons (emails, phones, custodian side…)
silicon_only403a Carbon called an endpoint for Silicons (/v1/me/webhook…: its own webhook, deliveries and replay)
account_not_active403the account isn't active (pending custodian, unfinished import)
app_credentials_required401an app endpoint got no credentials
invalid_app_credentials401unknown app_id, wrong secret, or malformed Basic header
app_disabled403 (400 in /v1/flows, 401 at userinfo)the app is disabled
app_mismatch403app credentials were used on another app's /v1/apps/{app_id} URL
not_app_owner403an account that isn't one of the app's authors (its owner or an accepted co-author) tried to manage it
unknown_app404 (400 in /v1/flows)no app has this app_id
request_token_required401GET /v1/silicons/requests/{id} without Bearer sarq_…
invalid_request_token401the sarq_ token doesn't belong to this request
internal_token_required401/v1/internal/* without the internal token (Silicon Apps only)
invalid_internal_token401the internal token is wrong
internal_api_disabled403the server has no internal token configured
access_removed401at userinfo: the account removed your app's access
membership_inactive401 at userinfo, 403 for proofsthe account has no active membership with your app

Rate limits and locks

CodeStatusCause and fix
rate_limited429over a limit (Limits); wait Retry-After / details.retry_after_seconds. The message names the limit ("the limit is 120 per minute"); id changes add details.limit, window_seconds, retry_at; imports add row budget details
verification_locked42310 wrong codes in a row for this address; every code to it waits 60 seconds (details.locked_until)
login_locked42310 wrong STKs in a row for this Silicon; sign-in waits 60 seconds
imports_busy503the server is already parsing its maximum of imports; retry after Retry-After (15 s)

Verification codes

CodeStatusCause and fix
invalid_code422wrong code (details.remaining_attempts for the address), or not 6 digits (not counted). The 10th wrong one in a row has remaining_attempts: 0, details.locked_until and Retry-After
code_expired410older than 10 minutes, or replaced by a resend; send a new one
code_already_used409this code was already accepted
challenge_not_found404unknown challenge_id (or one of another flow)
no_code_sent409a resend (or a requirement verify) before any code was sent in this flow

Ids and lookups

CodeStatusCause and fix
invalid_id422 (400 at by-id)not a valid c:/si: id, or the wrong kind; details.reason is invalid or reserved_word
id_taken409another account has it; details.suggestions lists free ones
id_reserved409it was changed away from recently and is held for 10 days (details.reserved_until)
invalid_uuid400not a uuid (an id was given: use /v1/accounts/by-id/{id})
account_not_found404no account with this uuid or current id; at CLI sign-in, no active Carbon with that email or phone (sign up first)
silicon_not_found404not a Silicon you are custodian of (other Carbons' Silicons are never revealed)
custodian_not_found404no active Carbon has the c:id named as custodian or transfer target; name them by email instead

Profile, photos and deletion

CodeStatusCause and fix
dob_immutable422a Silicon's date of birth is its creation day
confirmation_required422DELETE needs {"confirm": "<current id>"}
confirmation_mismatch422confirm isn't the account's current id; nothing was deleted
custodian_of_silicons409a Carbon who is custodian of Silicons can't be deleted (details.silicons); transfer or delete them first
custodian_required403a Silicon can't delete itself; its custodian does
photo_too_large413over 2 MB
empty_photo422an empty body
invalid_image422the bytes aren't a readable PNG, JPEG, WebP or GIF
photo_type_mismatch422the bytes are another format than Content-Type says (details.detected_content_type)
photo_dimensions_too_large422over 8192 px a side or 50 megapixels
photo_not_found404no such photo (or it was removed)

Emails, phones and linked identities

CodeStatusCause and fix
invalid_email / invalid_phone / invalid_country422the address, number or country code can't be read (the message says why)
email_in_use / phone_in_use409it belongs to another account; an address belongs to one account only
email_already_added / phone_already_added409it is already on your account
email_limit_reached / phone_limit_reached42210 already; remove one first
email_not_found / phone_not_found404not on your account
email_not_verified / phone_not_verified409only a verified address can be primary
cannot_remove_primary409make another address primary first
invalid_provider400the provider isn't google or apple
unknown_provider404the same, in a sign-in or link URL
identity_not_found404no such linked identity
identity_in_use409that Google/Apple account is linked to another account
last_sign_in_method409removing it would leave no way to sign in (no email or phone)
browser_session_required400linking Google/Apple needs the account site's browser session, not a token
provider_not_configured503no Google/Apple credentials for this app or deployment

Your apps, sessions and history

CodeStatusCause and fix
membership_not_found404you never signed into that app
first_party_app400 / 422the account site (silicon-accounts) can't lose access (400) or get a short-lived token (422)
session_not_found404not a session of yours (an app's sign-in is removed with DELETE /v1/me/apps/{app_id})
invalid_history_kind400kind isn't signin, id_change, custodian, proof, app_access or security
requirements_missing409the app requires a detail the account lacks (details.missing); add it, then retry

Hosted sign-in

CodeStatusCause and fix
redirect_uri_not_registered400the redirect_uri isn't registered exactly; never redirected to
invalid_scope400an unknown scope (with details.redirect_to)
unsupported_response_type400response_type other than code
method_not_enabled400 / 403the app didn't enable that method (or no managed credentials exist)
flow_not_found404unknown flow id
flow_not_bound403the request lacks this flow's sa_flow cookie: continue in the browser that started it
flow_expired410flows last 60 minutes; start again from the app
invalid_step409the flow is at another step (the message names the allowed ones)
flow_completed / flow_failed409the flow ended; GET /v1/flows/{id} returns its redirect_to
flow_changed409the flow moved on in another tab while this request ran, or the app changed its flow and the details page is gone: GET /v1/flows/{id} shows where it is now
account_changed409the browser is now signed in as a different account than the flow's
account_unavailable409the address belongs to an account that can't sign in
session_required401"continue as" without a browser session
continue_not_allowed403the app turned off remember_browser
reauthentication_required403the app asked for prompt=login
email_domain_not_allowed403the app accepts only some email domains
signup_not_allowed403the app takes no new accounts (allow_signup: false)
signup_not_bound403the sign-up belongs to another browser
signup_expired410sign-ups last 48 hours; verify the address again
signup_already_completed409this sign-up already created an account; sign in instead
detail_not_on_page409details/add for a detail that isn't on the page on screen
no_previous_page409details/back on the first page
requirements_missing409a required email or phone of the page isn't on the account yet (details.missing): add it with details/add + details/verify
invalid_state400a provider callback with a malformed state

Some codes come in flow.error (and in ?error= on your redirect URI) rather than as HTTP errors: login_required, consent_required, interaction_required (prompt=none), access_denied (cancelled on a details or review page), provider_cancelled, provider_error, provider_token_invalid, provider_unavailable, provider_config_changed, provider_answer_elsewhere, provider_email_invalid, email_not_verified, hosted_domain_mismatch (a Google account outside the app's google.hosted_domain), signup_expired, session_changed (linking), identity_in_use, email_in_use, email_limit_reached (linking, as ?link_error=).

Device sign-in

CodeStatusCause and fix
device_code_not_found404no device sign-in waits for this user code
device_code_used409already approved or denied
device_code_expired410user codes last 10 minutes; start the sign-in again
device_flow_off403the app turned device sign-ins off after the code was made: sign in to it another way
unauthorized_client400POST /v1/device/authorize named an app that hasn't turned on device_flow
invalid_client400POST /v1/device/authorize named an app that doesn't exist

Silicons and custodians

CodeStatusCause and fix
invalid_stk422at sign-in: not stk- + 8 to 32 hex characters (creating a Silicon or rotating its STK reports a bad chosen STK as validation_failed on stk)
invalid_credentials401unknown si:id or wrong STK (one answer for both)
custodian_pending403the custodian hasn't accepted yet (details.custodian, request_id, expires_at)
custodian_declined403the custodian declined; the account was released
custodian_expired403nobody accepted within 14 days; the account was released
custodian_request_not_found404no such request (or not addressed to you)
custodian_request_not_pending409already accepted, declined, expired or cancelled (details.status)
custodian_request_expired410the 14 days ran out
custodian_request_pending409the Silicon already has a pending request
silicon_not_pending409accepting an initial request for a Silicon that is no longer waiting
silicon_not_active409a transfer of a Silicon that isn't active
already_custodian409you already are its custodian
transfer_pending409one transfer at a time (details.request_id): cancel it first
transfer_not_found404no pending transfer to cancel
transfer_to_self422a transfer must go to another Carbon
transfer_stale409the custodian changed after the transfer was requested
webhook_not_set409a test ping, secret rotation or replay without a webhook URL: set one first
invalid_assertion401a key sign-in's assertion is malformed, expired, for another aud, not signed by a live key of that Silicon, or was used before: sign a fresh one
key_exists409the Silicon already has this key (details.key_id)
too_many_keys40910 live keys already: revoke one first
key_not_found404no key with this id belongs to the Silicon
app_not_allowed403the Silicon's custodian only lets it get short-lived tokens for the apps in details.allowed_apps: ask the custodian to add the app
unknown_app422an allow-list names an app that doesn't exist (details.unknown)
federation_exists409the Silicon already trusts these tokens (details.federation_id)
too_many_federations40920 live trusts already: remove one first
federation_not_found404no trust with this id belongs to the Silicon
issuer_unreachable422a new trust's issuer has no discovery document we can read over https from a public address, it names another issuer, or its jwks_uri isn't public https (details.issuer)
federated_session403a sign-in that came from a trusted outside token tried to add a key or a trust: do it from the STK or a key, or as the custodian
custodian_only403only the Silicon's custodian chooses its identity token audiences
audience_not_allowed403the Silicon's custodian hasn't allowed this audience for identity tokens (details.allowed_audiences)

Apps

CodeStatusCause and fix
config_version_conflict409the sign-in setup changed since the version you sent (details.current_version): re-read, re-apply, resend
user_not_found404the uuid isn't in this app's user base (uuids are case-sensitive)
import_not_found404no such import job for this app
delivery_not_found404no such webhook delivery for this app, or for this Silicon (/v1/me/webhook/deliveries…)
unknown_columns422the import has columns Silicon Accounts doesn't keep (details.unknown_columns, allowed_columns); remove them or set ignore_unknown_columns
duplicate_columns422the same column twice
no_identifier_columns422no email, emails, phone or phones column
empty_import422no rows
too_many_rows422over 100,000 rows
invalid_csv422the CSV can't be parsed (line given)
too_many_columns422over 200 columns
value_too_large422a value over 8 KB, or a column name over 200 bytes (details.row, details.column)
too_many_items422a JSON list over 50 items
owner_not_found / owner_email_conflict / owner_unavailable422 / 409 / 409Silicon Apps sync: the owner can't be resolved

Import rows carry their own message codes (missing_identifier, ambiguous_match, duplicate_in_file, external_id_conflict, id_conflict, …), and Import existing users lists them.

Subscriptions and the event stream

CodeStatusCause and fix
subscription_exists409the app already has a subscription with this delivery (details.subscription_id); an app has one webhook and one stream subscription at most: change that one instead
subscription_not_found404no subscription with this id belongs to the app
invalid_updates422updates names something that isn't an update (details.allowed lists them)
invalid_webhook_events422PUT /v1/apps/{app_id}/webhook events names something that isn't an update
stream_subscription_required409an app opened GET /v1/events/stream without a stream subscription: create one with POST /v1/apps/{app_id}/subscriptions {"delivery":"stream"}
unknown_event_id400the stream's Last-Event-ID or after isn't an event of this feed: resume with the last id this stream sent you, or connect without one
too_many_streams4295 streams are already open for this app or account: close one (one stream carries every event of the feed), then retry after Retry-After
stream_capacity_reached503this server is full or restarting: reconnect after Retry-After with Last-Event-ID

When we end a stream on purpose, we send event: stream.closed with a reason first. See Event stream.

Versions and capabilities

CodeStatusCause and fix
unsupported_version400the Accounts-Version header names a version this deployment doesn't serve (details.supported, details.current): send a supported one or leave the header out
capabilities_missing422GET /v1/capabilities?require=… named a capability that is unknown or unsupported (details.missing, details.supported, details.available)

Proofs

CodeStatusCause and fix
invalid_subject_token400the subject token isn't a live access token (details.reason: not_an_access_token, invalid, expired, revoked)
subject_token_wrong_app403the subject token belongs to another app (details.token_app)
app_verification_single_app422an app verification request named apps in audiences: an app verification proof is for exactly one app; send {"receiving_app": "…"} once per app (details.field, details.apps)
unknown_receiving_app400the receiving app doesn't exist (details.app_ids)
invalid_receiving_app400the issuer itself, or Silicon Accounts itself (silicon-accounts, developer)
receiving_app_disabled403the receiving app is disabled
invalid_proof_refresh_token400not a sapr_ token, or unknown (mistyped, another environment, or its proof ended over 30 days ago)
not_issuing_app403only the issuing app refreshes or revokes a proof
proof_refresh_token_reused400a used refresh token was presented: the proof is now revoked
proof_revoked410the proof was revoked, or its User verification sign-in ended (details.reason, revoked_at)
proof_expired410past the proof's lifetime
invalid_proof_id400not a UUID
proof_not_found404not a proof you can see

A proof that doesn't verify is never an error: POST /v1/proofs/verify answers 200 with {"valid": false, "expires_at": null}.

Server

CodeStatusCause and fix
internal500A fault on our side. Retry later. If it continues, report the details.request_id so we can find the request.
database_unavailable503the database is unreachable; nothing was changed; retry in a few seconds
request_timeout503the request ran past its time budget (30 s, 60 s for uploads, 5 min for imports)
web_not_built503(static hosting only) the account site build is incomplete
dev_outbox_disabled404the development outbox is off

When a request is refused by a framework layer rather than a handler, the code comes from the status: forbidden 403, conflict 409, gone 410, locked 423, not_acceptable 406, length_required 411, uri_too_long 414, range_not_satisfiable 416, not_implemented 501, bad_gateway 502, unavailable 503, gateway_timeout 504, request_failed (other 4xx).

OAuth errors

These come from /v1/oauth/token, /revoke and /introspect as {"error", "error_description"}, with Cache-Control: no-store.

errorStatusCause
invalid_request400 (413 for a body over 64 KB)a parameter is missing, repeated or malformed; the client authenticated twice
invalid_client401unknown app, wrong secret, disabled app, or no credentials; WWW-Authenticate: Basic realm="Silicon Accounts"
invalid_grant400the code, refresh token, SLT or device code is unknown, expired, already used (a reused refresh token or code also revokes its sign-in), revoked, another app's, or its account is deleted or removed the app's access; a redirect_uri or PKCE mismatch; a token exchange's outside token refused, with the reason and its code in brackets: invalid_federated_token (malformed, unsafe algorithm, bad signature, unknown key, expired, not yet valid, used before), no_matching_trust (no trust of the Silicon accepts its issuer, audience and claims), issuer_unavailable (the issuer's keys couldn't be read)
unauthorized_client400a public client (client_id without a secret) used a grant that needs the secret, an app without device_flow used the device-code grant, or an app asked for a token exchange (it signs a Silicon into Silicon Accounts itself)
unsupported_grant_type400the grant isn't supported (the description names the alternative)
invalid_scope400a refresh asked for more scopes than were granted
authorization_pending400device sign-in not approved yet; keep polling
slow_down400polled within 5 seconds of the last poll; add 5 seconds
access_denied400the Carbon denied the device sign-in
expired_token400the device code expired (10 minutes)
rate_limited429more than 60 token exchanges per minute from one address; wait Retry-After seconds
server_error500a fault on our side (request id in the description)
temporarily_unavailable503the request ran past its time budget

Rust client codes

silicon_accounts_client::Error::code() returns our code for API and OAuth errors, and its own codes for failures that never got a response:

CodeVariantMeaning
connection_failedError::HttpDNS, TLS, refused connection
request_timeoutError::Httpno answer within the client timeout (30 s by default)
unexpected_responseError::Decodean answer that isn't what this client expects (wrong URL, newer service)
invalid_inputError::InvalidInputrefused before sending (an empty STK, an http URL to a remote host…)
timed_outError::TimedOuta waiting helper gave up (the work goes on in the service)
token_malformed, token_unsupported_algorithm, token_unknown_key, token_invalid_key, token_bad_signature, token_expired, token_not_yet_valid, token_wrong_audience, token_wrong_issuer, token_missing_claimError::Tokenlocal access-token verification failed
http_<status>Error::Apia non-Silicon-Accounts error body (a proxy or load balancer answered)

Webhook verification (WebhookError) has EmptySecret, MissingHeader, InvalidTimestamp, TimestampOutOfTolerance (more than 5 minutes off), InvalidSignatureFormat, SignatureMismatch and InvalidBody. Refuse the delivery (answer 400 or 401) in every case. Details: Rust client.

The silicon-accounts CLI prints these same codes (with --json, as {"error":{"code","message","hint","exit_code","status","request_id","details"}}) and maps them to exit codes (Exit codes). It also has codes of its own, for problems it finds without asking us (not_signed_in, wrong_account_kind, invalid_arguments, corrupt_state_file, unknown_topic…), and CLI error codes lists them with their exit codes.

Related

Silicon Accounts · Reference · ReferenceHTTP API referenceFind any Accounts endpoint and who can call it, plus the rules every endpoint shares for errors, retries, pagination and limits.Silicon Accounts · Reference · ReferenceLimitsEvery limit we enforce, from request rates and lockouts to lifetimes, sizes and retention, and what you get back when you reach one.Silicon Accounts · Reference · ReferenceRust clientCall Silicon Accounts from Rust with silicon-accounts-client, with every type, method and helper and working examples for accounts, apps and custodians.Silicon Accounts · Reference · Referencesilicon-accounts CLI referenceEvery silicon-accounts command, flag, environment variable and exit code, with examples for reading results and handling failures in scripts.Silicon Accounts · Learn · ExplanationSecurityHow we protect credentials, sign-in sessions and webhook delivery, and what your app still needs to check itself.

Every page is plain Markdown at its address plus .md. Silicons can read llms.txt, llms-full.txt or the docs index, or call the MCP server.