Skip to main content

Handle verification errors

This guide shows you how to handle the four non-success PresentedCredentialsEvent statuses 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 a PresentedCredentialsEvent to your callback endpoint at the end of each presentation flow. The getting started guide covers parsing the event and extracting credentials from a FULFILLED response. This guide focuses on the four non-success statuses: REJECTED, EXPIRED, PROCESSING_ERROR, and VERIFICATION_FAILED.

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

Time to implement: 30 minutes to 1 hour.

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.

# Simulate a REJECTED callback
curl -X POST http://localhost:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "REJECTED", "state": "abc123", "errorDetails": "access_denied"}'

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

# Simulate a PROCESSING_ERROR callback
curl -X POST http://localhost: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://localhost:3000/callback \
-H "Content-Type: application/json" \
-d '{"status": "VERIFICATION_FAILED", "state": "abc123", "errorDetails": "invalid_signature"}'

REJECTED

The user declined the presentation request in their wallet. Log the errorDetails field (which contains the wallet's OAuth 2.0 error code), inform the user, and offer to retry.

Example user message: "You declined the verification request. Select 'Verify' to try again."

EXPIRED

The session timed out before the wallet responded. Create a new presentation request with a fresh state value.

Example user message: "Your verification session timed out. Select 'Verify' to start a new session."

PROCESSING_ERROR

An internal processing failure occurred (for example, a decryption, state validation, or infrastructure error). Log the errorDetails field, alert your operations team, and offer the user a retry.

Example user message: "Something went wrong during verification. Try again, and contact support if the problem persists."

VERIFICATION_FAILED

Credential verification failed (for example, invalid signature, revoked credential, or DCQL mismatch). The credentials and credentialsRaw fields are absent. Use the errorDetails field to diagnose the specific failure. Do not grant access.

Example user message: "Your credential could not be verified. Contact your credential issuer if you believe this is an error."

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.

Step 3: Map statuses to user-facing messages

StatusUser-facing messageAction
REJECTED"You declined the verification request. Select 'Verify' to try again."Log error, offer retry
EXPIRED"Your verification session timed out. Select 'Verify' to start a new session."Create new request
PROCESSING_ERROR"Something went wrong during verification. Try again, and contact support if the problem persists."Log error, offer retry
VERIFICATION_FAILED"Your credential could not be verified. Contact your credential issuer if you believe this is an error."Log error, deny access

Understanding errorDetails

The errorDetails field in REJECTED, PROCESSING_ERROR, and VERIFICATION_FAILED callbacks contains information about the underlying failure.

Callback statuserrorDetails contents
REJECTEDThe wallet's OAuth 2.0 error code and optional description, such as access_denied or access_denied: User canceled
PROCESSING_ERRORInternal failure details such as decryption errors or state validation failures
VERIFICATION_FAILEDCredential verification failure details such as invalid signatures, revoked credentials, or DCQL mismatches

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.

Testing

Test checklist

  • Callback handler processes all four 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

Troubleshooting

Callback receives unexpected status values

The connector delivers exactly one of the five documented statuses. 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 presentation request. Use the state value as the session key and overwrite it when retrying.

Next steps

Further reading