> For the complete documentation index, see [llms.txt](https://docs.empe.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.empe.io/develop/verifier/error-handling-and-troubleshooting.md).

# 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.

```json
{
  "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](/develop/verifier/authentication.md#development-mode).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.empe.io/develop/verifier/error-handling-and-troubleshooting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
