Skip to main content

Error Codes

Every error response from the Qpher API includes a machine-readable error_code that you can use for programmatic error handling, along with a human-readable message.

Error Response Format​

All errors follow a consistent structure:

{
"error": {
"error_code": "ERR_KEM_001",
"message": "Invalid encryption request"
},
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-01-15T10:30:00.000Z"
}
FieldTypeDescription
error.error_codestringStable, machine-readable code. Format: ERR_<CATEGORY>_###.
error.messagestringHuman-readable description. Do not parse this programmatically — it may change.
request_idstring (UUID)Unique request identifier for tracing and support.
timestampstring (ISO 8601)UTC time when the error occurred.

Error Code Naming Convention​

Error codes follow the pattern ERR_<CATEGORY>_<NUMBER>:

CategoryTypical HTTP StatusDomain
AUTH401Authentication failures
INVALID400, 422Input validation errors
FORBIDDEN403Authorization denied
NOT_FOUND404Resource not found
CONFLICT409Duplicate or concurrent operations
RATE_LIMIT429Rate limit exceeded
POLICY403Policy engine denied request
KEM400, 500KEM encryption/decryption errors
KMS400, 404, 409Key management errors
SIG400Signature sign/verify errors
CRYPTO500Internal cryptographic errors
INTERNAL500Unexpected internal errors
BAD_GATEWAY502Downstream service failure
SERVICE503Service unavailable
TIMEOUT504Request timeout

Complete Error Code Reference​

Use the search box to filter by error code or description, or select a category from the dropdown.

CodeHTTPCategoryDescriptionResolution
ERR_AUTH_001401AuthenticationMissing or invalid API keyInclude a valid API key in the X-API-Key header
ERR_AUTH_MFA_001401AuthenticationMFA verification requiredCall POST /api/v1/auth/mfa/verify with the challenge token and a TOTP or recovery code
ERR_MFA_001401MFAInvalid codeRe-enter the code. Message is generic in v1.4+ to prevent factor enumeration
ERR_MFA_002 deprecated401MFADeprecated — merged into ERR_MFA_001 (v1.4)Treat as ERR_MFA_001. Stop branching on this code
ERR_MFA_006 deprecated401MFADeprecated — merged into ERR_MFA_001 (v1.4)Treat as ERR_MFA_001. Stop branching on this code
ERR_MFA_011403MFAPlan gate — organization-wide MFA requires a paid planMove the organization to a paid plan to require MFA for every member
ERR_MFA_013401MFARe-verification requiredCall POST /api/v1/auth/step-up to obtain a fresh 5-minute single-use step-up token, then retry with the X-Step-Up-Token header
ERR_INVALID_001422ValidationRequest validation failedCheck the request body against the endpoint schema; the offending field is named in error.field
ERR_INVALID_003 deprecated422ValidationReserved — not currently emittedHandle ERR_INVALID_001 for validation failures
ERR_FORBIDDEN_001403AuthorizationOperation not allowed for planUpgrade your plan or reduce usage
ERR_NOT_FOUND_001404Not FoundResource not foundVerify the resource ID or key version exists
ERR_CONFLICT_001409ConflictResource already existsUse a unique identifier or update the existing resource
ERR_RATE_LIMIT_001429Rate LimitRate limit exceededWait and retry with exponential backoff
ERR_POLICY_001403PolicyPolicy engine denied requestCheck that your plan includes this endpoint and that the monthly limit has not been reached
ERR_KEM_001400KEMInvalid encryption requestEnsure plaintext is base64-encoded and key_version is valid
ERR_KEM_002400KEMInvalid decryption requestUse the same key_version that was used for encryption
ERR_KEM_003500KEMEncryption operation failedRetry the request; contact support if persistent
ERR_KEM_010400KEMInvalid wrap requestEnsure symmetric_key is base64-encoded, one of the allowed lengths, and key_version is valid
ERR_KEM_011 deprecated400KEMReserved — not currently emittedHandle ERR_KEM_010 for symmetric-key length/format errors
ERR_KEM_012400KEMInvalid unwrap requestUse the same key_version that was used for wrapping
ERR_KMS_001400KMSInvalid key operation requestUse supported algorithms: Kyber768, Dilithium3, X-Wing, Composite-ML-DSA
ERR_KMS_002 deprecated404KMSReserved — key-not-found returns ERR_NOT_FOUND_001Handle ERR_NOT_FOUND_001 for missing keys; list keys to find available versions
ERR_KMS_003404KMSArchived keyArchiving cannot be undone; Qpher no longer uses an archived key
ERR_KMS_020403KMSArchive is Portal-UI onlyArchive a key from the Qpher Portal UI; the API, SDKs, and MCP server intentionally cannot archive a key
ERR_SIG_001400SignatureInvalid sign requestEnsure message is base64-encoded and key is active
ERR_SIG_002400SignatureInvalid verify requestProvide all required fields for verification
ERR_SIG_010400SignatureInvalid sign-hash or verify-hash requestEnsure hash is base64-encoded and hash_algorithm is SHA-256, SHA-384, or SHA-512
ERR_SIG_011400SignatureUnsupported hash algorithmUse SHA-256, SHA-384, or SHA-512
ERR_CRYPTO_001500CryptoCryptographic operation failedRetry the request; contact support if persistent
ERR_INTERNAL_001500InternalInternal server errorRetry with backoff; contact support with request_id
ERR_BAD_GATEWAY_001502GatewayDownstream service errorRetry; contact support with request_id if persistent
ERR_SERVICE_001503ServiceService unavailable (fail-closed)Retry after a short delay; contact support if persistent
ERR_TIMEOUT_001504TimeoutGateway timeoutRetry with a simpler request or contact support

MFA / Step-Up Changes (v1.4)​

  • ERR_MFA_013 — new. Returned when a sensitive endpoint is called without a valid X-Step-Up-Token. See Authentication → Step-Up Re-Verification.
  • ERR_MFA_002 and ERR_MFA_006 — deprecated. Merged into ERR_MFA_001 ("Invalid code") to prevent factor-type enumeration from error messages. The legacy codes remain in the catalogue for a six-month deprecation window; treat any of these as equivalent to ERR_MFA_001.
  • ERR_MFA_011 description clarified — this code is reserved for the organization-level "require MFA" plan gate (a paid plan is required). Earlier versions overloaded it; step-up uses ERR_MFA_013 instead.

Using Request IDs for Debugging​

Every response — success or error — includes a request_id. This ID traces the request through the entire Qpher infrastructure and is essential for debugging.

Always log request IDs

Store the request_id from every API response in your application logs. When something goes wrong, this is the fastest way to get help from the Qpher support team.

Best practices​

  1. Log every request ID on your side, even for successful responses. This creates an audit trail you can cross-reference later.

  2. Include the request ID in support tickets. When contacting Qpher support, provide the request_id and the approximate timestamp. This allows the team to locate the exact request in the distributed tracing system.

  3. Send your own request ID by including the X-Request-ID header. The API will use your value instead of generating one, making it easier to correlate requests across your own systems:

curl -X POST https://api.qpher.ai/api/v1/kem/encrypt \
-H "X-API-Key: qph_your_key_here" \
-H "X-Request-ID: my-trace-id-12345" \
-H "Content-Type: application/json" \
-d '{"plaintext": "SGVsbG8="}'

Handling Errors in Your Application​

Retry strategy​

Not all errors should be retried. Use this guide:

Error CategoryRetry?Strategy
ERR_AUTH_*NoFix your API key or credentials.
ERR_INVALID_*NoFix the request payload.
ERR_FORBIDDEN_*NoCheck plan tier or policy configuration.
ERR_NOT_FOUND_*NoVerify the resource exists.
ERR_CONFLICT_*MaybeWait briefly, then retry if the conflict is transient.
ERR_RATE_LIMIT_*YesWait for Retry-After seconds, then retry.
ERR_POLICY_*NoCheck your tenant's policy configuration.
ERR_KEM_* / ERR_SIG_*Depends400 errors: fix input. 500 errors: retry with backoff.
ERR_KMS_*Depends400/404: fix input. 409: wait and retry.
ERR_CRYPTO_*YesRetry with exponential backoff.
ERR_INTERNAL_*YesRetry with exponential backoff.
ERR_BAD_GATEWAY_*YesRetry after a short delay.
ERR_SERVICE_*YesRetry after a short delay.
ERR_TIMEOUT_*YesRetry with a simpler request or increased patience.

Exponential backoff example​

import time
import requests

def call_with_retry(url, headers, json_body, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=json_body)

if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
time.sleep(retry_after)
continue

if response.status_code >= 500:
time.sleep(2 ** attempt)
continue

return response

raise Exception(f"Failed after {max_retries} retries")
Persistent errors

If an error persists, contact support@qpher.ai with the request_id from the response.