Guides

Error Handling

The SDK throws structured AfricaZKError instances with stable error codes. Map each one to the right user-facing message and recovery path.

Error codes

CodeThrown byCauseRecovery
INVALID_INPUTverifyIdentity()idType / idNumber / dob failed format validationShow inline form errors; user fixes input
BACKEND_FAILEDverifyIdentity()Dojah could not confirm the IDShow banner; suggest user double-check NIN/BVN
CIRCUIT_FAILEDgenerateProof()Constraints failed (age < 18 or invalid signature) or WASM errorRestart from verifyIdentity()
PROOF_REJECTEDsubmitProof()Anchor program rejected the proof or public keyRestart from verifyIdentity() — proof was tampered or stale
DUPLICATE_NULLIFIERsubmitProof()Identity already attested to another walletDirect user to use the existing verified wallet — cannot proceed
USER_REJECTEDsubmitProof()User cancelled the wallet signatureShow retry button; no state changed
RPC_FAILEDsubmitProof() / checkAttestation()RPC node unreachable or timed outRetry with exponential backoff; offer manual retry after 3 fails

Recommended pattern

error-router.ts
import { AfricaZKError } from '@africazk/identity'

try {
  await runFlow()
} catch (e) {
  if (e instanceof AfricaZKError) {
    switch (e.code) {
      case 'INVALID_INPUT':       return showFormError(e.message)
      case 'BACKEND_FAILED':      return showBanner('Verification failed — check your details')
      case 'CIRCUIT_FAILED':      return showBanner('Proof generation failed — please reload')
      case 'PROOF_REJECTED':      return showBanner('Proof was rejected on-chain')
      case 'DUPLICATE_NULLIFIER': return showFinal('This identity already has a verified wallet')
      case 'USER_REJECTED':       return showRetry('You cancelled the wallet signature')
      case 'RPC_FAILED':          return showRetry('Network error — please try again')
    }
  } else {
    showBanner('Something unexpected happened')
    console.error(e)
  }
}

Default to friendly, not technical

Users do not care about "BACKEND_FAILED". They care that they couldn't verify. Translate every code into a sentence a non-technical user can act on, and put the raw code into a small "details" toggle for support tickets.

When to retry automatically

  • RPC_FAILED — yes, with exponential backoff (1s, 2s, 4s, then surface).
  • BACKEND_FAILED — no. The user input failed Dojah verification; retrying with the same input gives the same result.
  • CIRCUIT_FAILED — no. The credential is dead; restart from verifyIdentity().
  • USER_REJECTED — no. Wait for the user to click retry. Auto-retrying re-pops the wallet and annoys.

Errors you should never see in production

If you see PROOF_REJECTED with valid SDK inputs, something is wrong: either the SDK is out-of-date relative to the deployed program, or the local proof verifier is broken. Open an issue.