Skip to content

Error Reference Index

This section provides a comprehensive reference for all error responses returned by the AnkaSecure API. Each error includes a unique URI, HTTP status code, and detailed resolution guidance.

Quick Reference Table

HTTP Code Error Type URI
400 Invalid Input https://docs.ankatech.co/errors/invalid-input
400 Invalid PKCS#7 Structure https://docs.ankatech.co/errors/invalid-pkcs7
400 Missing Request Part https://docs.ankatech.co/errors/missing-request-part
400 Validation Error https://docs.ankatech.co/errors/validation
400 Unsupported Keystore Format https://docs.ankatech.co/errors/unsupported-keystore-format
400 Key Operation Incompatible https://docs.ankatech.co/errors/key-operation-incompatible
400 Invalid Operation Name https://docs.ankatech.co/errors/invalid-operation-name
400 Signature Mismatch https://docs.ankatech.co/errors/signature-mismatch
400 Query Range Exceeded https://docs.ankatech.co/errors/query-range-exceeded
400 Invalid Request https://docs.ankatech.co/errors/invalid_request
400 Invalid Grant https://docs.ankatech.co/errors/invalid_grant
400 Unsupported Grant Type https://docs.ankatech.co/errors/unsupported_grant_type
400 Invalid Scope https://docs.ankatech.co/errors/invalid_scope
400 Tenant Selection Required https://docs.ankatech.co/errors/tenant_selection_required
401 Unauthorized https://docs.ankatech.co/errors/unauthorized
401 Invalid Client https://docs.ankatech.co/errors/invalid_client
401 Unauthorized Client https://docs.ankatech.co/errors/unauthorized_client
402 Payment Required https://docs.ankatech.co/errors/payment-required
403 Forbidden https://docs.ankatech.co/errors/forbidden
403 SaaS-Only Feature https://docs.ankatech.co/errors/saas-only-feature
403 Unsupported Principal Type https://docs.ankatech.co/errors/unsupported-principal-type
403 Access Denied https://docs.ankatech.co/errors/access_denied
404 Resource Not Found https://docs.ankatech.co/errors/not-found
404 Material Version Not Found https://docs.ankatech.co/errors/material-version-not-found
405 Method Not Allowed https://docs.ankatech.co/errors/method-not-allowed
409 Conflict https://docs.ankatech.co/errors/conflict
409 Invalid Key State https://docs.ankatech.co/errors/invalid-key-state
409 Concurrent Modification https://docs.ankatech.co/errors/concurrent-modification
409 Data Integrity Error https://docs.ankatech.co/errors/data-integrity
409 Duplicate KID https://docs.ankatech.co/errors/duplicate-kid
409 Cascade Subset Violation https://docs.ankatech.co/errors/cascade-subset-violation
409 Deployment Policy Locked https://docs.ankatech.co/errors/locked-deployment-policy
409 Counterparty Type Still Referenced https://docs.ankatech.co/errors/counterparty-type-referenced
409 Marketplace Bootstrap In Progress https://docs.ankatech.co/errors/marketplace-bootstrap-in-progress
409 Key Protection Backend Not Configured https://docs.ankatech.co/errors/key-protection-backend-not-configured
412 Precondition Failed https://docs.ankatech.co/errors/precondition-failed
413 Payload Too Large https://docs.ankatech.co/errors/payload-too-large
413 License Artifact Too Large https://docs.ankatech.co/errors/license-artifact-too-large
415 Unsupported Media Type https://docs.ankatech.co/errors/unsupported-media-type
422 Unprocessable Entity https://docs.ankatech.co/errors/unprocessable-entity
422 Unsupported PKCS#7 Format https://docs.ankatech.co/errors/unsupported-pkcs7-format
422 Missing Private Key https://docs.ankatech.co/errors/missing-private-key
422 Decryption Failed https://docs.ankatech.co/errors/decryption-failed
422 Purpose Required https://docs.ankatech.co/errors/purpose-required
422 Rotation Purpose Violation https://docs.ankatech.co/errors/rotation-purpose-violation
422 Key Operations Narrowing Forbidden https://docs.ankatech.co/errors/key-ops-narrowing-forbidden
422 Rotation Security Downgrade https://docs.ankatech.co/errors/rotate-security-downgrade
422 Dual Not Applicable https://docs.ankatech.co/errors/dual-not-applicable
422 Composite Wire Shape Mismatch https://docs.ankatech.co/errors/composite-wire-shape-mismatch
422 Composite Component Algorithm Mismatch https://docs.ankatech.co/errors/composite-component-algorithm-mismatch
422 Invalid State Transition https://docs.ankatech.co/errors/invalid-state-transition
422 Invalid Material Transition https://docs.ankatech.co/errors/material-status-invalid-transition
422 Invalid Stable KID Transition https://docs.ankatech.co/errors/stable-kid-status-invalid-transition
422 Purpose Mismatch https://docs.ankatech.co/errors/purpose-mismatch
422 Algorithm Not Permitted for Operation https://docs.ankatech.co/errors/algorithm-not-permitted-for-operation
422 Tenant Type Restriction https://docs.ankatech.co/errors/tenant-type-restriction
422 Tenant Policy Subset Violation https://docs.ankatech.co/errors/tenant-policy-subset-violation
422 Lifecycle Policy Subset Violation https://docs.ankatech.co/errors/lifecycle-policy-subset-violation
422 Import Operation Not Orchestrable https://docs.ankatech.co/errors/import-operation-not-orchestrable-by-use-case
422 Composite-Pair Operation Not Supported https://docs.ankatech.co/errors/composite-pair-operation-requires-multi-key-use-case-not-yet-supported
422 License Artifact Invalid https://docs.ankatech.co/errors/license-artifact-invalid
422 License Deployment Mismatch https://docs.ankatech.co/errors/license-deployment-mismatch
422 License Expired https://docs.ankatech.co/errors/license-expired
422 Identity Provider Email Unverified https://docs.ankatech.co/errors/identity-provider-email-unverified
400/404/409/422 Upstream Rejected Request https://docs.ankatech.co/errors/upstream-rejected-request
429 Too Many Requests https://docs.ankatech.co/errors/too-many-requests
429 Refresh Cooldown Active https://docs.ankatech.co/errors/refresh-cooldown
429 Key Protection Rate Limited https://docs.ankatech.co/errors/key-protection-rate-limited
4xx Client Error https://docs.ankatech.co/errors/client-error
500 Internal Server Error https://docs.ankatech.co/errors/internal
500 Cryptographic Error https://docs.ankatech.co/errors/crypto
500 Repository Error https://docs.ankatech.co/errors/repository
500 Admin Operation Failed https://docs.ankatech.co/errors/admin-operation
500 Keystore Error https://docs.ankatech.co/errors/keystore
500 Data Access Error https://docs.ankatech.co/errors/data-access
501 Not Implemented https://docs.ankatech.co/errors/not-implemented
502 Upstream Service Unavailable https://docs.ankatech.co/errors/upstream-unavailable
503 Service Unavailable https://docs.ankatech.co/errors/service-unavailable
503 Async Not Usable https://docs.ankatech.co/errors/async-not-usable
503 Data Source Unavailable https://docs.ankatech.co/errors/data-source-unavailable
503 Marketplace Temporarily Disabled https://docs.ankatech.co/errors/marketplace-temporarily-disabled
503 Snapshot Stale https://docs.ankatech.co/errors/snapshot-stale
503 Stats Snapshot Unavailable https://docs.ankatech.co/errors/stats-snapshot-unavailable
503 Audit Event Publishing Failed https://docs.ankatech.co/errors/kafka-publish-failure
503 Key Protection Backend Unavailable https://docs.ankatech.co/errors/key-protection-backend-unavailable
503 Key Protection Backend Misconfigured https://docs.ankatech.co/errors/key-protection-backend-misconfigured
504 Upstream Service Timeout https://docs.ankatech.co/errors/upstream-timeout
504 Gateway Timeout https://docs.ankatech.co/errors/gateway-timeout

Error Response Format

All API errors follow the RFC 7807 Problem Details standard and are returned with Content-Type: application/problem+json. The body is a flat JSON object — there is no nested error envelope:

{
  "type": "https://docs.ankatech.co/errors/error-type",
  "title": "Human-readable error title",
  "status": 422,
  "detail": "Additional context about the error",
  "instance": "/api/v3/migration/convert-pkcs7-to-jose",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": 1730000000
}

The core fields are defined by RFC 7807:

  • type - URI identifying the error category (links to the matching page in this reference)
  • title - Short, human-readable summary of the error type
  • status - HTTP status code, repeated in the body for convenience
  • detail - Human-readable explanation specific to this occurrence
  • instance - The request path that produced the error

Two platform extensions accompany the standard fields:

  • correlationId - Request correlation identifier, for correlation with server-side logs
  • timestamp - Epoch-seconds timestamp of when the error was produced

Some errors add further extensions (for example, PKCS#7 conversion errors include an errorCode and recipient metadata; OAuth 2.0 errors mirror the RFC 6749 error/error_description fields). See the individual error pages for details.

OAuth 2.0 token-endpoint errors (invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope, tenant_selection_required, access_denied) use an underscore type suffix so the RFC 7807 type matches the RFC 6749 error code exactly.

Where extension members live depends on which service answered

RFC 7807 — and its successor RFC 9457 — define extension members as top-level members of the problem object. This platform does not place them uniformly, so a client must know which service produced the body before it reads a non-RFC member. Reading the wrong one returns undefined, and that is the single most common cause of a client falling back to parsing detail.

What is uniform: the five RFC-defined members — type, title, status, detail, instance — are top-level in every service, exactly as the specification requires, and timestamp is top-level everywhere it is emitted, even though by the RFC's definition it too is an extension member.

What moves is the correlation identifier:

Service Correlation member Where to read it
Core API, PQC Handshake API correlationId top-level — response.correlationId
Admin API, Audit API requestId nested — response.extensions.requestId
Auth API, License Server none in the body the X-Correlation-Id response header

The X-Correlation-Id and X-Request-Id response headers are set by every service, so a client that does not want to branch on the service can correlate from the headers alone.

Core API / PQC Handshake API shapecorrelationId is a first-class top-level member (this is the canonical example at the top of this page). extensions carries only the additional per-error members, such as the PKCS#7 conversion errorCode and its recipient metadata:

{
  "type": "https://docs.ankatech.co/errors/unsupported-pkcs7-format",
  "title": "Unsupported PKCS#7 Format",
  "status": 422,
  "detail": "The requested target format is not compatible with the parsed structure.",
  "instance": "/api/v3/migration/convert-pkcs7-to-jose",
  "correlationId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": 1751500800,
  "extensions": {
    "errorCode": "INCOMPATIBLE_TARGET_FORMAT"
  }
}

Admin API / Audit API shape — there is no top-level correlationId. The correlation identity is extensions.requestId, so a client must read response.extensions.requestId, not response.requestId. Every other extension member on these two services is likewise nested:

{
  "type": "https://docs.ankatech.co/errors/invalid-input",
  "title": "Invalid Input",
  "status": 400,
  "detail": "Field 'algorithm' is required and must be one of the catalog values.",
  "instance": "/api/v3/admin/tenants/2f1c9d84-6b2e-4d3a-9f57-0a1b2c3d4e5f/keys",
  "timestamp": 1751500800,
  "extensions": {
    "requestId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

extensions is absent when an error carries none, so read it defensively on every service. Two bodies are produced by a different mapper and carry no extensions and no timestamp at all — the 502 Upstream Service Unavailable and the 504 Upstream Service Timeout.

Dispatch on type — not on errorCode, and never on detail

type is the primary identifier of a problem. RFC 9457 §3.1.1 defines it that way, every distinct condition in this platform has its own dedicated slug, and — decisively — type is the only discriminator that survives every path a response can take to you.

Some Core API and PQC Handshake API bodies additionally carry extensions.errorCode, a stable machine-readable string. It is convenient for a direct integration, but it is not a substitute for type, because it is dropped when the Admin API relays a Core API refusal: the relay's problem view is a closed four-member record — type, title, status, detail — so no extension member of the originating service crosses that boundary. A consumer keyed on errorCode therefore works against the Core API directly and silently stops discriminating on the relayed path.

type crosses it unchanged. So:

  • Branch on type. It is stable, it is documented on the page each type URI names, and it is identical on the direct and the relayed path.
  • Use errorCode only as a refinement, when it is present, and only where two conditions genuinely share one type. Never as the primary key, and never assume it is present.
  • Never parse detail. It is prose written for a human, it is localized, and it is the one member whose exact wording carries no compatibility promise. If you find yourself matching on its text, the discriminator you actually need is a type — please report it so a dedicated one can be published.
// Correct: the type URI is the contract.
switch (problemDetails.getType().toString()) {
    case "https://docs.ankatech.co/errors/key-ops-narrowing-forbidden": widenKeyOpsAndResubmit(); break;
    case "https://docs.ankatech.co/errors/purpose-required":            declarePurposeAndResubmit(); break;
    default:                                                            logAndSurface(problemDetails);
}

detail is a sentence, never a document

detail is always a single human-readable sentence. It is never a nested JSON document, and it is never an error body from another service folded into a string. This holds even when the rejection was detected by an internal service that the one you called forwarded your request to — see S2S Relay Envelope for exactly which members come from where in that case. Where a rejection carries per-item detail, that detail is published as a named extension member (for example extensions.failedEntries), not as text inside detail.

406 Not Acceptable carries no body

Content negotiation is the one deliberate exception to "every error is a problem document". When your Accept header excludes every media type an endpoint can produce, the response is a bare 406 Not Acceptable: status line only, Content-Length: 0, and no Content-Type header.

There is no type slug for it, and it is absent from the table above by design. RFC 9110 §15.5.7 explicitly permits a 406 with no representation, and an empty body is the only response that is representable under every Accept value — including the value that caused the condition. Sending a application/problem+json body to a client that has just declared it cannot accept that type would be self-contradictory.

Practically, for a strict JSON client:

  • Accept: application/json on an error-only endpoint returns the endpoint's normal status, not a 406.
  • Accept: application/xml (or any value the endpoint cannot satisfy) returns the empty 406.
  • A malformed Accept value also returns the empty 406, not a 400: an unparseable header makes the mapping fail to match on its producible types, which is the same condition as an unsatisfiable one.
  • */* and an absent Accept header behave exactly as before.

Treat a 406 as a client-side content-negotiation bug: correct the Accept header and retry. Do not attempt to parse the response.

The Browser-Redirect Error Surface

Three external identity-federation legs are top-level browser navigations, not API calls. When one of them fails, the browser is redirected to the console login screen carrying ?error=<code>&ref=<uuid> — there is no response body, and therefore no type, no detail and no correlationId (the correlation identifier is ref). See Federated Login Errors for the six codes and their remedies.

This is a second error representation, not an exception to the RFC 7807 guarantee above. A surface may use it if and only if all three of the following hold:

  1. the request is a top-level browser navigation that the browser renders as a document;
  2. the caller therefore cannot read a response body, so an interposing CDN or proxy may replace it;
  3. an equivalent typed RFC 7807 refusal remains available on a non-navigation surface for programmatic consumers.

Every non-navigation surface — fetch/XHR, the SDKs, the CLIs and service-to-service calls — returns RFC 7807 Problem Details unconditionally. No error type documented on the pages below is retired, renumbered or made unavailable to a programmatic caller by the redirect projection.

Client Errors (4xx)

Client errors indicate that the request contains incorrect syntax or cannot be fulfilled due to client-side issues.

400 Bad Request

401 Unauthorized

  • Unauthorized - Missing or invalid authentication credentials
  • Invalid Client - OAuth 2.0: OAuth 2.0 client authentication failed
  • Unauthorized Client - OAuth 2.0: OAuth 2.0 client is not authorized to use the requested grant type

402 Payment Required

  • Payment Required - License expired or usage limits exceeded, payment or renewal needed

403 Forbidden

404 Not Found

405 Method Not Allowed

409 Conflict

412 Precondition Failed

413 Payload Too Large

415 Unsupported Media Type

422 Unprocessable Entity

429 Too Many Requests

4xx Generic

  • Client Error - General client error fallback for unspecified 4xx status codes

Server Errors (5xx)

Server errors indicate that the server failed to fulfill a valid request.

500 Internal Server Error

A 500 means the platform could not attribute the failure to your request. It is a server fault, and it is not retryable — a transient condition is reported as a 503 with Retry-After, never as a 500. A condition that has an owning error type is reported as that type, with its own status.

501 Not Implemented

502 Bad Gateway

503 Service Unavailable

504 Gateway Timeout

  • Upstream Service Timeout - An internal upstream dependency did not respond within the operation's deadline; retryable
  • Gateway Timeout - An upstream dependency did not respond within the operation's deadline; retryable

Error Handling Best Practices

Retry Strategy

  • 4xx errors: Generally should NOT be retried without fixing the request
  • 500 errors: NOT retryable. A 500 is an unattributable server fault, not a transient one — the identical request produces the identical result. Record the correlation identifier and report it instead
  • 502/504 errors: Retryable - honor the Retry-After header when present (emitted while the circuit breaker toward the failing upstream is open), otherwise apply exponential backoff
  • 503 errors: Retryable when a Retry-After header is present — wait the stated delay. A 503 without Retry-After signals a durable misconfiguration (for example key-protection-backend-misconfigured) and must not be retried: retrying re-presents the same bad configuration, and against a PKCS#11 token a repeated wrong PIN locks the token's user PIN
  • 429 errors: Honor the Retry-After header before retrying

Retrying a 503 is more aggressive than retrying a 500

In the AnkaSecure SDK and CLI, 502, 503 and 504 classify as a transient server error — three automatic retries, no prompt — while a 500 classifies as a plain server error with two retries behind a prompt. Several key-protection conditions that used to surface as a 500 now surface as a 503, which means they draw more automatic client traffic, not less. Honor Retry-After, and treat a 503 that carries no Retry-After as non-retryable.

Error Logging

Always log the following from error responses:

  • correlationId - For correlation with server-side logs
  • timestamp - For temporal analysis
  • type - For programmatic error handling
  • detail - For debugging context

Programmatic Error Handling

This pattern applies to API errors — every response carrying an application/problem+json body. It does not apply to the browser-redirect error surface, which has no body and therefore no type to dispatch on; those failures are read from the error query parameter by the console, not by a programmatic client.

// Example: dispatch on the RFC 7807 `type` URI
switch (problemDetails.getType().toString()) {
    case "https://docs.ankatech.co/errors/payment-required":
        redirectToPaymentPortal();
        break;
    case "https://docs.ankatech.co/errors/validation":
        displayFieldErrors(problemDetails.getDetail());
        break;
    case "https://docs.ankatech.co/errors/not-found":
        handleMissingResource();
        break;
    default:
        logErrorAndNotifyUser(problemDetails);
}

Rate Limiting

When encountering rate limit errors:

  1. Check the X-RateLimit-Remaining header
  2. Respect the X-RateLimit-Reset timestamp
  3. Implement client-side throttling
  4. Consider upgrading your plan for higher limits

Additional Resources

Need Help?

If you encounter an error not documented here or need assistance resolving persistent errors:

  1. Check the correlationId from the error response
  2. Review server status at status.ankatech.co
  3. Contact support with the error details and correlationId
  4. Consult the Developer Hub Reference for endpoint-specific requirements