API Error Codes - ericfitz/tmi GitHub Wiki
API Error Codes
Every TMI error response carries a machine-readable error code. The codes form a closed, documented vocabulary defined in the OpenAPI specification (Error and OAuthError schemas), so clients can branch on them. This page describes the vocabulary and how to use it.
Decision record: tmi#1048.
Overview
The vocabulary has three parts:
- REST codes (
Error.error): a closed enum used by every REST endpoint. Each code names one HTTP semantic. - Domain reasons (
details.code): an open list of lowercase snake_case reasons a client can act on, nested under a REST code. - Protocol codes (
OAuthError.error): used only by/oauth2/*,/saml/*and/.well-known/*, where RFC-defined codes apply.
The HTTP status of a response and its error code always agree with the tables below. WebSocket message codes are a separate vocabulary and are not covered here.
Response shapes
REST error (Error schema). error and error_description are required; error_uri and details are optional.
{
"error": "conflict",
"error_description": "A group with this name already exists",
"details": {
"code": "duplicate_group",
"suggestion": "Choose a different group name"
}
}
Protocol error (OAuthError schema), with the same optional fields:
{
"error": "invalid_grant",
"error_description": "The authorization code is invalid or expired"
}
Branch on error first. Use details.code, when present, for finer handling. Do not parse error_description; it is for humans and can change.
REST error codes (Error.error)
| Code | HTTP status | Meaning |
|---|---|---|
invalid_input |
400 | A body, field, header or query value fails validation (including pagination bounds and JSON syntax) |
invalid_id |
400 | A path or query identifier is malformed (UUID, slug) |
invalid_patch |
400 | A JSON Patch document is malformed, targets a disallowed path, or cannot be applied. Resync and rebuild the patch rather than fixing a field |
unauthorized |
401 | Missing, expired or invalid credentials |
insufficient_user_authentication |
401 | Step-up authentication required (RFC 9470) |
forbidden |
403 | Authenticated but not permitted, including protected objects and self-deletion |
not_found |
404 | The resource or route does not exist, or is hidden by authorization |
method_not_allowed |
405 | HTTP method not supported for the endpoint |
not_acceptable |
406 | Requested representation not supported |
conflict |
409 | State conflict: duplicate, in use, or wrong lifecycle state |
version_mismatch |
409 | If-Match does not match the current version; reload and retry |
gone |
410 | Permanently removed (for example, audit data past retention) |
payload_too_large |
413 | Request body exceeds the size limit |
unsupported_media_type |
415 | Content-Type not accepted |
unprocessable_entity |
422 | Well-formed request that cannot be processed in the current state (for example, provider not registered, embedding dimension mismatch) |
if_match_required |
428 | The If-Match header is missing |
rate_limit_exceeded |
429 | Transient limit reached; honor Retry-After |
quota_exceeded |
403 or 429 | Hard cap reached; retrying does not help |
server_error |
500 | Unexpected failure, including panic recovery |
not_implemented |
501 | Operation not supported by this server |
service_unavailable |
503 | A dependency (database, cache, provider) is unavailable; retry later |
Legacy top-level domain codes
Two domain codes also appear in the Error.error enum because deployed clients branch on the top-level field. Each is also set in details.code, so clients can migrate. They will move to details.code only in a future breaking change.
| Code | HTTP status | Meaning |
|---|---|---|
feature_not_available |
404 | The requested feature is not available on this server |
content_token_provider_not_configured |
422 | The content token provider is not configured; the response also carries a top-level provider_id |
Domain reasons (details.code)
details.code is an open list: new reasons can be added without a schema version change, so treat unknown values as the generic case for the REST code. Values are lowercase snake_case.
| REST code (status) | details.code values |
|---|---|
invalid_input (400) |
picker_file_id_mismatch, invalid_picker_registration, invalid_challenge, invalid_provider_type, client_callback_required, client_callback_not_allowed, invalid_if_match, invalid_version, duplicate_header, unsupported_encoding, reserved_key |
unauthorized (401) |
token_not_linked_or_failed, invalid_token (identity link token) |
forbidden (403) |
protected_group, provider_mismatch |
not_found (404) |
session_not_found, feature_not_available |
conflict (409) |
duplicate_group, duplicate_membership, self_deletion, protected_user, deletion_blocked, encryption_not_enabled, unreadable_settings_limit, session_not_active |
unprocessable_entity (422) |
provider_not_registered, provider_not_configured, content_token_provider_not_configured, dimension_mismatch, inconsistent_dimensions, no_source, access_request_not_supported |
rate_limit_exceeded (429) |
too_many_connections, session_full, message_rate_limit, duplicate_invocation |
quota_exceeded (429) |
session_limit_exceeded |
server_error (500) |
websocket_upgrade_failed |
service_unavailable (503) |
llm_busy, llm_not_configured |
Protocol error codes (OAuthError.error)
These routes use the OAuthError schema: /oauth2/{authorize,token,refresh,revoke,introspect,userinfo,callback,step_up}, /saml/* and /.well-known/*. The TMI-defined routes /oauth2/content_callback, /oauth2/providers* and /me/identities/link/* use the REST codes above.
On these routes invalid_request means an RFC 6749 malformed protocol request, not a REST validation failure.
| Group | Codes |
|---|---|
| RFC 6749 | invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope, access_denied, unsupported_response_type, server_error, temporarily_unavailable |
| RFC 7009 | unsupported_token_type |
| RFC 9470 | insufficient_user_authentication |
| TMI extensions (RFC 6749 section 8.5) | identity_mismatch, account_conflict, email_not_verified, provider_unreachable, provider_response_invalid, invalid_provider |
| TMI SAML extensions | saml_error, saml_not_enabled, saml_unavailable, saml_provider_not_found, saml_metadata_error, saml_init_error, saml_invalid_logout_request, saml_logout_error |
| Transport codes from route-agnostic middleware | unauthorized, not_found, method_not_allowed, not_acceptable, payload_too_large, unsupported_media_type, rate_limit_exceeded, server_error |
invalid_token and insufficient_scope (RFC 6750) are values in the WWW-Authenticate response header, not response body codes.
Request-validation failures from the Unicode validation middleware return invalid_request on protocol routes and invalid_input on REST routes.
Migration from earlier codes
Earlier releases emitted undocumented synonyms. HTTP statuses did not change. If your client matched any of the old values, update it as follows.
| Old code | New code |
|---|---|
invalid_request (REST routes) |
invalid_input |
validation_failed, validation_error, invalid_limit, invalid_offset |
invalid_input |
invalid_uuid |
invalid_id |
invalid_format, patch_failed |
invalid_patch |
internal_error, internal_server_error |
server_error |
too_many_requests |
rate_limit_exceeded |
too_many_connections, session_full, message_rate_limit |
rate_limit_exceeded, with the old value in details.code |
session_limit_exceeded |
quota_exceeded, with the old value in details.code |
Identity link invalid_token (401) |
unauthorized, with invalid_token in details.code |
Identity link invalid_provider |
invalid_input |
Identity link temporarily_unavailable |
service_unavailable |
| Other domain-specific codes | The REST code for the same HTTP status, with the old value in details.code (except the two legacy codes above) |
Human sentences in the error field |
A vocabulary code, with the sentence in error_description |
On protocol routes, too_many_requests became rate_limit_exceeded and service_unavailable became the RFC code temporarily_unavailable in token handling.
See also
- API Overview: common status codes and error format
- REST API Reference
- API Integration: client error handling