For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 dcqlQuery or presentationDefinition is required. Other causes include a dcqlQuery or presentationDefinition value that is not a JSON object, or omitting signingDid when the verifierId path 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 no kid).

  • 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 the admin role from realm_access.roles (client roles in resource_access.<client>.roles are also accepted); a non-admin token receives Insufficient 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:web document that the service is not serving (the hosted-document routes return DID 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 unresolvable verifierId: these surface as a 500 rather than a 404, 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 dcqlQuery or presentationDefinition, and that the chosen form is well-formed. A 400 with the message Either dcqlQuery or presentationDefinition must be provided. means both were absent. A structurally invalid query that is still a JSON object fails downstream and returns a 500 instead — 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 behind OIDC_JWKS_URL. A 401 points to the token itself; a 403 with Insufficient role means the token is valid but lacks the admin role 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 a 500 with 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:web issuers, the document must be reachable over HTTPS at the URL derived from the DID (for did:web:<domain>:<uuid>, https://<domain>/<uuid>/did.json; bare-domain DIDs use /.well-known/did.json); an unreachable host or a did.json that is not served will cause credential verification to fail. did:key and did:jwk resolve 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. 500 responses 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