Skip to content

Errors

View .md

A failed request returns a machine-readable code, a human-readable message, and optional metadata. Branch on code; messages can change, codes stay stable.

{ "code": "PROJECT_NOT_FOUND", "message": "Project not found", "meta": { "projectId": 123 } }
Field Type Meaning
code string Machine-readable code
message string Human-readable description
meta object Optional context, varies by error

When the body, params or query fail schema validation, the API returns 422 with code and meta and no message. Only the first failing field is reported.

{ "code": "FIELD_REQUIRED", "meta": { "field": "name" } }
Code Cause meta fields
FIELD_REQUIRED A required field is missing field
FIELD_TOO_SHORT Below the minimum length field, minLength
FIELD_TOO_LONG Above the maximum length field, maxLength
VALIDATION_ERROR Wrong type, format, pattern, range, or enum value field, plus one of expectedType, expectedFormat, expectedPattern, minimum, maximum, allowedValues
Status Meaning
400 The request body or parameters are invalid
401 Missing or invalid authentication
403 Valid credentials but insufficient permissions
404 The resource does not exist
409 The request conflicts with the current state of a resource
413 An uploaded file exceeds the size limit
422 Schema validation failed
429 Rate limit exceeded
500 Unexpected server error
502 An upstream provider (billing, LLM) failed
504 An upstream provider timed out

The status column lists every status a code is sent with.

Code Status When
AUTHENTICATION_REQUIRED 401 No valid session or API key
UNAUTHORIZED 401 Authentication was attempted and failed
INVALID_CREDENTIALS 401, 400 Email or password is incorrect. 400 when confirming your current password
INVALID_OR_EXPIRED_TOKEN 400 A reset or verification token is invalid or expired
API_KEY_INVALID 401 API key is malformed or does not exist
API_KEY_DISABLED 401 API key has been deactivated
API_KEY_EXPIRED 401 API key has passed its expiration date
API_KEY_PERMISSIONS_REVOKED 401 The key’s creator no longer holds the permissions it was granted
EMAIL_NOT_VERIFIED 403 Verify the email address before using the API
USER_BLOCKED 403 The account has been blocked
RECENT_SIGN_IN_REQUIRED 403 Sign in again before performing this action
Code Status When
FORBIDDEN 403 Access denied. The most common permission failure
INSUFFICIENT_PERMISSION 403 The role lacks the permission this action requires
NO_PROJECT_ACCESS 403, 404 No access to this project. 404 when the user is not assigned to it
NOT_ORGANIZATION_MEMBER 403, 404, 400 Not a member of the organization. 403 when it is the caller, 404 when it is the target user
ROLE_HIERARCHY_VIOLATION 403, 400 Cannot assign or modify a role above your own. 400 when a project role would exceed the organization role
INVALID_ROLE 403 The role is not valid for this action
IMPERSONATION_FORBIDDEN 403 Not allowed while impersonating
Code Status When
NOT_FOUND 404 Generic resource not found
USER_NOT_FOUND 404 User does not exist
ORGANIZATION_NOT_FOUND 403, 404 Organization does not exist. 403 when the caller is not a member
PROJECT_NOT_FOUND 403, 404 Project does not exist. 403 when the caller has no access to it
API_KEY_NOT_FOUND 404 API key does not exist
LOCALE_NOT_FOUND 400, 404 Locale does not exist. 400 when it arrives as an invalid input code
TRANSLATION_KEY_NOT_FOUND 404 Translation key does not exist
NAMESPACE_NOT_FOUND 404 Namespace does not exist
WEBHOOK_NOT_FOUND 404 Webhook does not exist
WEBHOOK_DELIVERY_NOT_FOUND 404 Webhook delivery does not exist
INVITATION_NOT_FOUND 404 Invitation does not exist or has already been used
SUBSCRIPTION_NOT_FOUND 404 No subscription found
PLAN_NOT_FOUND 404 The billing plan does not exist

Most “already exists” conditions are 400, not 409.

Code Status When
CONFLICT 409 The request conflicts with current state
TRANSACTION_CONFLICT 409 The database rolled the transaction back. Retry the request as-is
EMAIL_ALREADY_USED 409 An account with this email already exists
LOCALE_ALREADY_EXISTS 409 The locale is already added to the project
SUBSCRIPTION_MANUALLY_MANAGED 409 The plan is admin-managed, so self-serve billing changes are refused
SUBSCRIPTION_ALREADY_EXISTS 409 The organization already has a live subscription. Fix or cancel it instead of starting a new checkout
UNIQUE_CONSTRAINT 400 A name that must be unique is already taken
USER_ALREADY_IN_ORGANIZATION 400 User is already a member of this organization
INVITATION_ALREADY_SENT 400 An invitation has already been sent to this email
TRANSLATION_KEY_ALREADY_EXISTS 400 A key with this name already exists in the namespace
PASSWORD_ALREADY_SET 400 Password has already been set for this account
Code Status When
VALIDATION_ERROR 422, 400, 409 Schema validation at 422. Business-rule failures use 400; a concurrent-edit conflict uses 409
FIELD_REQUIRED 422 A required field is missing
FIELD_TOO_SHORT 422 Below the minimum length
FIELD_TOO_LONG 422 Above the maximum length
INVALID_INPUT 400 Input data is invalid
SYNTAX_ERROR 400 Request body is not valid JSON
Code Status When
FILE_TOO_LARGE 413 Uploaded file exceeds the size limit
FILE_TYPE_NOT_ALLOWED 400 File format is not supported
INVALID_FILE_STRUCTURE 400 File content does not match the expected structure
TOO_MANY_KEYS 400 Import exceeds the maximum number of translation keys
ZIP_BOMB_DETECTED 400 Compressed file detected as potentially malicious
JSON_BOMB_DETECTED 400 JSON file detected as potentially malicious
IMPORT_SESSION_EXPIRED 401 The import session timed out. Start the import again
IMPORT_SESSION_MISMATCH 403 Import confirmation does not match the session that started it
Code Status When
TOO_MANY_REQUESTS 429 Any limit the rate-limit plugin enforces: the global limit, every per-route override, or repeated failed authentication. From the global limiter, meta.expiresIn is a human-readable wait such as "1 minute". From the failed-authentication limiter, meta.retryAfter is a number of seconds and there is no expiresIn
RATE_LIMIT_EXCEEDED 429 A route-specific limit the handler enforces itself, such as type generation or the import session bucket
Code Status When
READ_ONLY_MODE 403 The organization is in read-only mode. Every write endpoint can return this
LIMIT_EXCEEDED 403 Plan limit reached, such as max keys, projects, or members. meta carries metric, current, and limit
FEATURE_NOT_AVAILABLE 403 Feature not available on the current plan
ACTIVE_SUBSCRIPTION_REQUIRED 400 A paid subscription is still linked, so this change is refused
CANNOT_CANCEL_FREE_PLAN 400 Cannot cancel a free plan
BILLING_NOT_INITIALIZED 500 The organization has no billing record
Code Status When
CANNOT_REMOVE_LAST_OWNER 400 Cannot remove or demote the last owner
OWNED_FREE_ORG_LIMIT_EXCEEDED 403 Free plan users can own at most one organization
INVITATION_EXPIRED 400 The invitation has expired
INVALID_INVITATION 403 The invitation was sent to a different email address
CANNOT_DELETE_LAST_PROVIDER 400 Cannot remove the only authentication provider
Code Status When
CANNOT_REMOVE_DEFAULT_LOCALE 400 Cannot remove the source language from a project
LOCALE_NOT_ENABLED 400 The locale is not enabled for this project
NOT_ENOUGH_KEYS 422 Too few keys to generate an AI context draft
FAILED_TO_CREATE_PROJECT 500 Internal error during project creation
Code Status When
WEBHOOK_URL_NOT_ALLOWED 400 The target URL is not a permitted destination
WEBHOOK_RETRY_NOT_ALLOWED 400 Only deliveries with failed status can be retried
Code Status When
INTERNAL_ERROR 500, 502, 504 Unexpected internal error. 502 when an upstream provider failed, 504 when it timed out
INTERNAL_SERVER_ERROR 500 General server error
CACHE_INVALIDATION_FAILED 500 A permission change was refused because its cache flush failed
UNKNOWN 500, 413 An error Comvi did not raise itself, with the status the server gave it: 413 for a body over the route’s limit, otherwise 500. In production a 5xx message is replaced with Internal server error
Header Meaning
Retry-After Seconds to wait before retrying
X-RateLimit-Limit Maximum requests in the window
X-RateLimit-Remaining Requests remaining in the window

Every limit the rate-limit plugin enforces sends these headers: the global limit and every per-route override, including machine translation, CDN deploys, AI context, and the auth and account routes. RATE_LIMIT_EXCEEDED, raised by handlers, and the 429 from repeated failed authentication carry the wait in meta instead.