Skip to main content

Handle errors

This guide shows you how to handle non-success callback statuses for both verification and issuance flows in your callback endpoint. You implement a handler that maps each error outcome to a user-facing message and add retry logic for recoverable errors.

Prerequisites

Overview

The connector delivers callback events to your endpoint at various points during verification and issuance flows—for example, when an issuance offer is created and when the flow completes. The verification quickstart covers parsing verification events and extracting credentials from a FULFILLED response, and the credential issuance guide covers handling the ISSUED status. This guide focuses on the non-success statuses for both flows.

For the complete event schema including all payload fields, see the callback events reference.

Time to implement: 30 minutes to 1 hour.

Verification errors

The connector delivers a PresentedCredentialsEvent at the end of each presentation flow. The four non-success statuses are REJECTED, EXPIRED, PROCESSING_ERROR, and VERIFICATION_FAILED.

Step 1: Handle each error status

Add a branch for each non-success status in your callback handler. The credentials and credentialsRaw fields are absent for all four statuses. The errorDetails field is present for REJECTED, PROCESSING_ERROR, and VERIFICATION_FAILED.

StatusWhat happenederrorDetailsYour responseExample user message
REJECTEDUser declined the request in their walletWallet's OAuth 2.0 error code (for example, access_denied)Log error, offer retry"You declined the verification request. Select 'Verify' to try again."
EXPIREDSession timed out before the wallet respondedAbsentCreate new request"Your verification session timed out. Select 'Verify' to start a new session."
PROCESSING_ERRORInternal failure (decryption, state validation, infrastructure)Internal failure detailsLog error, alert ops, offer retry"Something went wrong during verification. Try again, and contact support if the problem persists."
VERIFICATION_FAILEDCredential verification failed (invalid signature, expired certificate, revoked credential, or DCQL query mismatch)Verification failure detailsLog error, deny access"Your credential could not be verified. Contact your credential issuer if you believe this is an error."
# Simulate a REJECTED callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "REJECTED", "state": "abc123", "errorDetails": "access_denied"}'

# Simulate an EXPIRED callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "EXPIRED", "state": "abc123"}'

# Simulate a PROCESSING_ERROR callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "PROCESSING_ERROR", "state": "abc123", "errorDetails": "decryption_failed"}'

# Simulate a VERIFICATION_FAILED callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "VERIFICATION_FAILED", "state": "abc123", "errorDetails": "invalid_signature"}'

Step 2: Implement retry logic

When a recoverable error occurs (REJECTED, EXPIRED, or PROCESSING_ERROR), create a new presentation request with a fresh state value. Do not retry the same session—expired and rejected sessions cannot be reused.

# Create a new presentation request to retry verification
curl -X POST http://connector:8081/oidc4vp \
-H "Content-Type: application/json" \
-d '{
"dcql_query": {
"credentials": [
{
"id": "pid_identity",
"format": "dc+sd-jwt",
"meta": { "vct_values": ["urn:eudi:pid:1"] },
"claims": [
{ "path": ["given_name"] },
{ "path": ["family_name"] },
{ "path": ["birthdate"] }
]
}
]
}
}'

Example response:

{
"state": "def456",
"same_device_request_uri": "openid4vp://?client_id=...&request_uri=https%3A%2F%2Fconnector.example.com%2Foidc4vp%2Fdef456%2Frequest%3Fflow_type%3Dsame-device",
"cross_device_request_uri": "openid4vp://?client_id=...&request_uri=https%3A%2F%2Fconnector.example.com%2Foidc4vp%2Fdef456%2Frequest%3Fflow_type%3Dcross-device"
}

Store the new state value and display the new QR code or deep link to the user.

Understanding errorDetails

The OID4VP specification defines error codes that wallets return when they cannot fulfill a presentation request. The connector maps these wallet-reported errors to the REJECTED status and includes the original error code in errorDetails. Log this field to diagnose integration issues at the protocol level.

For the complete list of wallet-facing error codes, see the error codes reference.

Issuance errors

The connector delivers issuance events to your callback endpoint at multiple points during each issuance flow. The credential issuance guide covers handling the ISSUED status. This section focuses on the two non-success statuses: FAILED and EXPIRED.

Step 1: Handle each error status

Add a branch for each non-success status in your issuance callback handler. The errorDetails field is present for FAILED.

StatusWhat happenederrorDetailsYour responseExample user message
FAILEDIssuance failed (issuer service unavailable, internal error)Failure reasonLog error, alert ops, offer retry"Something went wrong while issuing your credential. Try again, and contact support if the problem persists."
EXPIREDSession TTL expired before the wallet completed the flowAbsentCreate new credential offer"Your issuance session timed out. Select 'Issue' to start a new session."
# Simulate a FAILED issuance callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{
"eventId": "abc123def456",
"status": "FAILED",
"offerId": "abc123def456",
"errorDetails": "issuer service error: signing key validation failed"
}'

# Simulate an EXPIRED issuance callback
curl -X POST http://backend.example.com:3000/callback \
-H "Content-Type: application/json" \
-d '{
"eventId": "abc123def456",
"status": "EXPIRED",
"offerId": "abc123def456"
}'

Step 2: Implement retry logic

When a FAILED or EXPIRED callback occurs, create a new credential offer. Do not retry the same offer—expired and failed sessions cannot be reused.

# Create a new credential offer to retry issuance (returns 201 Created)
curl -X POST http://connector:8081/offers \
-H "Content-Type: application/json" \
-d '{
"credential_configuration_id": "IdentityCredential",
"claims": {
"given_name": "Erika",
"family_name": "Mustermann",
"document_number": "T22000129",
"issuing_country": "DE"
}
}'

Example response (201 Created):

{
"offer_id": "xyz789abc012",
"credential_offer_uri": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fissuer.example.com%2Foidc4vci%2Foffers%2Fxyz789abc012"
}

Store the new offer_id and display the new credential_offer_uri as a QR code or deep link.

Understanding errorDetails

The errorDetails field in FAILED callbacks describes the infrastructure or processing failure. Log this field and alert your operations team.

During the issuance protocol exchange, the connector and the authorization server also return standardized OID4VCI error codes directly to the wallet. These protocol-level errors are not delivered to your callback endpoint. For the complete list of wallet-facing issuance error codes, see the error codes reference.

Testing

Test checklist

Verification:

  • Callback handler processes all four verification error statuses without errors
  • REJECTED and EXPIRED statuses trigger retry flow with a new state
  • REJECTED, PROCESSING_ERROR, and VERIFICATION_FAILED statuses log errorDetails
  • User-facing messages display for each status
  • VERIFICATION_FAILED status denies access

Issuance:

  • Callback handler processes FAILED and EXPIRED issuance statuses without errors
  • FAILED status logs errorDetails
  • FAILED and EXPIRED statuses trigger retry flow with a new credential offer
  • User-facing messages display for each status

Troubleshooting

Callback receives unexpected status values

The connector delivers exactly one of the documented statuses for each flow. If your handler receives an unrecognized value, log it and treat it as an error. Check that you are running a compatible connector version.

Wallet returns unsupported response mode or algorithm errors

The connector operates under the HAIP profile, which mandates the direct_post.jwt response mode and ES256 for signing. If the wallet does not support these requirements, it returns an error at the protocol level. Check that the wallet supports the HAIP profile. These errors typically appear as REJECTED callbacks with OID4VP error codes in the errorDetails field.

Retry creates duplicate sessions

Ensure you invalidate the old session before creating a new request or offer. Use the state value (verification) or offer_id (issuance) as the session key and overwrite it when retrying.

Next steps

Further reading