Repository navigation
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.
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.
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.
| 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 |
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
|
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
|
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.
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.
- API Overview: common status codes and error format
- REST API Reference
- API Integration: client error handling
- Using TMI for Threat Modeling
- Accessing TMI
- Authentication
- Identity Linking
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS)
- Deploying TMI Server
- Oracle Container Images
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Bootstrapping Production
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Configuring Local Development
- Managing Operational Settings
- Content Extractors - Limits and Overrides
- Database Operations
- Database Security Strategies
- Transaction Isolation
- Oracle Content Feedback FK Cleanup
- Security Operations
- Secret Rotation
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Local Development Cluster
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions
- Issue Tracker Integration
- Webhook Integration
- Addon System
- MCP Integration
- Delegated Content Providers
- Setting Up Google Content Providers
- API Clients
- API Client Maintenance
- Database Tool Reference
- TMI Terraform Analyzer
- WebSocket Test Harness