Error Handling and Troubleshooting
The Verifier returns clear HTTP status codes and a consistent JSON error shape when something goes wrong. A global exception filter wraps every failure in the same envelope, so your integration can parse errors uniformly regardless of which endpoint produced them.
Common Error Codes
400 Bad Request: Invalid input or a malformed request body. The most common cause when creating an authorization request is supplying neither query form — at least one of
dcqlQueryorpresentationDefinitionis required. Other causes include adcqlQueryorpresentationDefinitionvalue that is not a JSON object, or omittingsigningDidwhen theverifierIdpath parameter is not itself a DID. The global validation pipe also rejects request bodies containing unknown JSON properties (property X should not exist).401 Unauthorized: Missing, malformed, or expired OIDC bearer token. Every non-public endpoint requires
Authorization: Bearer <token>, validated against your identity provider's JWKS endpoint (OIDC_JWKS_URL). A token is rejected here if its signature does not verify against the JWKS keys, if it is expired, or if it is missing or malformed (for example, a token whose header has nokid).403 Forbidden: The token is valid but the principal lacks the required role. Administrative routes (DID, issuer, verifier, and holder management under
/agent/*and/holders/*) additionally enforce theadminrole fromrealm_access.roles(client roles inresource_access.<client>.rolesare also accepted); a non-admin token receivesInsufficient role. The core Verifier endpoints (authorization requests, verification sessions, and/credentials/verify) require a valid token but no specific role.404 Not Found: A mistyped or unmatched route path (the default response for an unknown route), or a request for a hosted
did:webdocument that the service is not serving (the hosted-document routes returnDID Document not found).500 Internal Server Error: An unexpected condition, configuration error, or a failed downstream operation such as DID resolution. A structurally invalid DCQL query or Presentation Exchange definition (a JSON object that does not conform to the query language's schema) also surfaces as a
500, because the structure is validated downstream by the underlying OpenID4VP library rather than by the service's own input validation. This also covers an unknown verification session id or an unresolvableverifierId: these surface as a500rather than a404, because the underlying record-not-found errors are not mapped to a not-found status. In production the response message is generic; consult your server logs for the underlying cause.
Example Error Response
The service returns the same envelope for every error: statusCode, message, path, and timestamp. For HttpException-based failures the message may also be an array of validation strings.
{
"statusCode": 400,
"message": "Either dcqlQuery or presentationDefinition must be provided.",
"path": "/verifiers/did:web:verifier.example.com:9b2c1d3e-4f5a-6789-abcd-ef0123456789/authorization-requests",
"timestamp": "2026-06-22T10:15:30.123Z"
}There is no code field. Parse statusCode for the HTTP status and message for the human-readable reason.
Troubleshooting Tips
Validate your credential query. Ensure your request includes exactly one of
dcqlQueryorpresentationDefinition, and that the chosen form is well-formed. A400with the messageEither dcqlQuery or presentationDefinition must be provided.means both were absent. A structurally invalid query that is still a JSON object fails downstream and returns a500instead — validate your DCQL or Presentation Exchange definition against its specification before sending.Check your bearer token. Confirm the
Authorization: Bearer <token>header is present, the token is unexpired, and it was issued by the provider behindOIDC_JWKS_URL. A401points to the token itself; a403withInsufficient rolemeans the token is valid but lacks theadminrole required by administrative routes.Verify session, nonce, and state. When a presentation is rejected, confirm the wallet is responding to the correct authorization request — presentations are bound to their originating request by nonce and state, and a mismatch will fail verification. Note that requesting an unknown or expired verification session id does not return a
404; it currently surfaces as a500with a generic message in production, so check your server logs to distinguish a missing session from another fault.Confirm DID resolution. Verification relies on resolving the issuer's DID document. For
did:webissuers, the document must be reachable over HTTPS at the URL derived from the DID (fordid:web:<domain>:<uuid>,https://<domain>/<uuid>/did.json; bare-domain DIDs use/.well-known/did.json); an unreachable host or adid.jsonthat is not served will cause credential verification to fail.did:keyanddid:jwkresolve from the identifier itself.Check transport security. OpenID4VP requires HTTPS in production. Plain HTTP is only permitted in development when explicitly enabled via
ALLOW_INSECURE_HTTP.Mind the rate limit. The service applies a global limit of 60 requests per 60 seconds. High-volume flows that exceed it receive
429 Too Many Requests; add backoff and retry logic accordingly.Review server logs.
500responses return a generic message in production; the detailed stack trace and request path are recorded in the service logs.
To rule out token problems while debugging locally, use the development bypass described in Authentication.
Last updated