Skip to main content

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"
}
FieldWhat to do with it
typeStable URI for the problem category — branch program logic on this, not on detail
titleShort human-readable summary
statusMirrors the HTTP status code
detailOccurrence-specific explanation — surface in logs, show to developers, never parse
instanceThe specific request that failed
serviceVersionInclude in support tickets
subproblemsArray 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) selectiveDisclosure keys not matching credentialData keys. The detail field 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, and serviceVersion for every non-2xx response.
  • Include your externalId so errors correlate to business records (Using externalId).
  • Never log credentials, offerUrl, authorizationUrl, qrCodeDataUrl, or VP tokens (Security Best Practices).