Error Handling
Every iDen2 error response — Credential Factory and Verification Gateway alike — uses the same structure: an RFC 9457 Problem Details object. Build one error handler and reuse it across both APIs.
The Problem Details object
{
"type": "https://api.iden2.net/problems/invalid-input",
"title": "Invalid Input",
"status": 400,
"detail": "offerId must be a valid UUID",
"instance": "/api/v1/iaas/credential-offers/not-a-uuid",
"serviceVersion": "1.0.0"
}
| Field | What to do with it |
|---|---|
type | Stable URI for the problem category — branch program logic on this, not on detail |
title | Short human-readable summary |
status | Mirrors the HTTP status code |
detail | Occurrence-specific explanation — surface in logs, show to developers, never parse |
instance | The specific request that failed |
serviceVersion | Include in support tickets |
subproblems | Array of nested Problem objects for compound validation errors — iterate it to report every field error at once |
Full field reference: API Reference Overview.
Decide: retry, fix, or escalate
Client errors — fix, don't retry (400, 404, 409, 413)
The request itself is wrong; resending it unchanged fails identically.
400— malformed JSON, missing required field, invalid UUID, field over max length, or (Credential Factory)selectiveDisclosurekeys not matchingcredentialDatakeys. Thedetailfield names the problem.404— the resource doesn't exist or belongs to another tenant. A correct-looking UUID that 404s usually means the wrong credentials for that resource.409(Verification Gateway Wallet API) — the session already reached a terminal state; a second VP submission is rejected.413(Verification Gateway Wallet API) — VP token exceeds size limits.
Auth errors (401, 403)
401— missing, invalid, or expired access token. Re-acquire a token and retry (Authentication).403— valid token, insufficient permission: either the token is missing a required scope, or your configuration doesn't allow the operation (e.g. issuing a credential type it isn't permitted to). Request the right scope; if it persists, it's a configuration matter — contact iDen2.
Gone (410)
The offer or session TTL elapsed. The resource will never become valid again — create a new one. Treat 410 exactly like a status of expired.
Server errors — retry with backoff (500, 503)
Transient platform or downstream issues. Retry with exponential backoff and jitter:
async function withRetry(fn, {retries = 4, baseMs = 500} = {}) {
for (let attempt = 0; ; attempt++) {
try {
return await fn();
} catch (err) {
const retryable = err.status === 500 || err.status === 503;
if (!retryable || attempt >= retries) throw err;
const delay = baseMs * 2 ** attempt + Math.random() * baseMs;
await new Promise((r) => setTimeout(r, delay));
}
}
}
Cap total retry time well below your offer/session TTL — there's no point retrying a create call for longer than the user will wait.
Don't confuse transport errors with domain failures
A Verification Gateway session that returns HTTP 200 with status: "failed" is not an HTTP error — it's a successful API call reporting a domain outcome. Handle failureReason (issuer_not_trusted, revoked_credential, ...) as business logic, not in your HTTP retry layer. The full enum and recommended actions are in the Verification Integration Guide.
Logging checklist
- Log
type,status,detail,instance, andserviceVersionfor every non-2xx response. - Include your
externalIdso errors correlate to business records (Using externalId). - Never log credentials,
offerUrl,authorizationUrl,qrCodeDataUrl, or VP tokens (Security Best Practices).