Unprocessable Entity
URI: https://docs.ankatech.co/errors/unprocessable-entity
HTTP Status: 422 Unprocessable Entity
When you see this
The server understood your request and the syntax was correct, but it could not process the content because of semantic or integrity issues.
Common Causes
-
Malformed JWE header
The flattened JWE JSON is invalid or missing required fields (protected,iv,recipients, orencrypted_keyfor non-diralgorithms). -
Malformed Compact JWE
The incoming JWE string cannot be parsed (e.g. does not conform to RFC 7516). -
Malformed Compact JWS
The incoming JWS string cannot be parsed (e.g. does not conform to RFC 7515). -
Detached JWS header validation failure
The detached-JWS JSON is missing itsprotectedsection orkidfield, or isn’t valid Base64-encoded JSON. -
Ciphertext integrity failure
AES-GCM/CCM tag mismatch or asymmetric unwrap padding error (OAEP/PKCS#1) during decrypt/unwrapping.
Response Example
{
"type": "https://docs.ankatech.co/errors/unprocessable-entity",
"title": "Unprocessable Entity",
"status": 422,
"detail": "The ciphertext failed its AES-GCM integrity check.",
"instance": "/api/v3/admin/tenants/2f1c9d84-6b2e-4d3a-9f57-0a1b2c3d4e5f/keys/my-mlkem-key",
"timestamp": 1751500800,
"extensions": {
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
}
Content-Type: application/problem+json — the response follows RFC 7807 Problem Details. The example above is an Admin API body, where the correlation identity is extensions.requestId; the Core API and the PQC Handshake API instead emit a top-level correlationId. Read the member your service emits — see Error Index & Overview.
Per-entry failures are a named member, never text inside detail
A keystore import in which every entry fails publishes those entries as the failedEntries extension member, so a client reads structured data rather than parsing a sentence. detail remains a single sentence, and it names how many entries this response carries and where to read them:
{
"type": "https://docs.ankatech.co/errors/upstream-rejected-request",
"title": "Upstream Rejected Request",
"status": 422,
"detail": "The key-management service rejected the request. This response carries 2 failed keystore entries under extensions.failedEntries.",
"instance": "/api/v3/admin/tenants/2f1c9d84-6b2e-4d3a-9f57-0a1b2c3d4e5f/keys/import-keystore",
"timestamp": 1751500800,
"extensions": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"failedEntries": [
{
"kid": "legacy-signing-key",
"sourceAlias": "firma",
"entryType": "PRIVATE_KEY",
"failureReason": "CERTIFICATE_VALIDATION_FAILED",
"errorMessage": "Certificate validation failed for kid 'legacy-signing-key'"
},
{
"kid": "legacy-encryption-key",
"sourceAlias": "cifrado",
"entryType": "PRIVATE_KEY",
"failureReason": "UNEXPECTED_ERROR",
"errorMessage": "The entry could not be imported due to an internal error. The cause is recorded in the server log."
}
]
}
}
Each entry carries only the fields listed above (kid, entryType, sourceAlias, failureReason, errorMessage); the list is capped at 50 entries so the response stays bounded. Read it at response.extensions.failedEntries.
errorMessage is never a raw internal diagnostic. Where the failure is a typed domain outcome — a certificate that did not validate, for example — the sentence describes that outcome and names only identifiers you supplied. Where it is an unexpected internal fault, the sentence is a fixed curated one (shown verbatim in the second entry above) and the diagnosable cause is recorded server-side instead; correlate it through extensions.requestId or the X-Request-Id header when contacting support.
The count in detail describes this response, not the upstream rejection: it is the number of entries published under extensions.failedEntries, which the 50-entry cap may have bounded. The sentence reads entry in the singular when exactly one entry is published.
Why this example carries the upstream-rejected-request type
The keystore-import 422 originates in the internal key-management service, and that service's 422 body is a keystore-import result document rather than a problem document — it carries the per-entry failures array the failedEntries member is projected from, but no type and no detail. So type and title are the responding service's own, and detail is an admin-authored sentence counting the entries this response publishes. The "returned no usable problem description" wording of the degenerate branch is reserved for a rejection from which no entry could be extracted — using it here would deny the structure the same response carries. The rule that matters to a client is unchanged — read type to classify, read extensions.failedEntries for the per-entry data, and never parse detail.
How to Resolve
- Validate token syntax
- Ensure your JWE/JWS strings exactly match the expected RFC formats.
- Check header JSON
- Decode the Base64 header and confirm all required fields (
kid,alg,iv, etc.) are present.
- Decode the Base64 header and confirm all required fields (
- Confirm cryptographic parameters
- IV length must be 12 bytes for AES-GCM; tags must be 16 bytes.
- Use correct key material
- Make sure you’re using the same
kidand algorithm that encrypted or signed the data.
- Make sure you’re using the same
- Re-submit
- After correcting any header or token issues, retry your request.
If you continue to receive a 422 error after these steps, please consult the full Developer Hub Reference.