Error Codes
PEAC Protocol defines error codes across 13 categories. All errors follow the RFC 9457 (Problem Details for HTTP APIs) format, providing machine-readable error responses with stable error codes.
Error format
Every PEAC error response conforms to the RFC 9457 structure:
RFC 9457 Error Response
{
"type": "https://www.peacprotocol.org/errors/E_INVALID_SIGNATURE",
"title": "Invalid Signature",
"status": 400,
"detail": "Receipt signature verification failed",
"instance": "/api/verify",
"peac_error_code": "E_INVALID_SIGNATURE"
}
| Field | Description |
|---|---|
type | Canonical URL for the error type |
title | Human-readable error title |
status | HTTP status code |
detail | Human-readable explanation of the specific error |
instance | URI reference identifying the specific occurrence |
peac_error_code | Stable PEAC error code for programmatic handling |
Categories
| Category | Description |
|---|---|
verification | Signature and key verification |
validation | Format, claim, and field validation |
infrastructure | JWKS fetch, rate limiting, circuit breaker |
control | Policy engine authorization decisions |
identity | Agent identity attestation |
attribution | Source attribution and content hashing |
bundle | Evidence bundle creation and validation |
dispute | Dispute evidence and resolution |
interaction | Interaction evidence schema |
workflow | Workflow DAG and step validation |
verifier | Verifier-side checks and policy |
Verification errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_INVALID_SIGNATURE | Invalid Signature | 400 | No |
E_KEY_NOT_FOUND | Key Not Found | 400 | No |
Validation errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_INVALID_FORMAT | Invalid Format | 400 | No |
E_EXPIRED | Receipt Expired | 400 | No |
E_NOT_YET_VALID | Not Yet Valid | 400 | Yes |
E_INVALID_ISSUER | Invalid Issuer | 400 | No |
E_INVALID_AUDIENCE | Invalid Audience | 400 | No |
E_INVALID_AMOUNT | Invalid Amount | 400 | No |
E_INVALID_CURRENCY | Invalid Currency | 400 | No |
E_INVALID_RAIL | Invalid Payment Rail | 400 | No |
E_MISSING_REQUIRED_CLAIM | Missing Required Claim | 400 | No |
E_EVIDENCE_NOT_JSON | Evidence Not JSON-Safe | 400 | No |
E_INVALID_SUBJECT | Invalid Subject | 400 | No |
E_INVALID_RECEIPT_ID | Invalid Receipt ID | 400 | No |
E_MISSING_EXP | Missing Expiration | 400 | No |
Wire 0.2 errors
Wire 0.2 introduces additional error codes for structured validation:
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_ISS_NOT_CANONICAL | Issuer Not Canonical | 400 | No |
E_INVALID_KIND | Invalid Kind | 400 | No |
E_INVALID_TYPE | Invalid Type | 400 | No |
E_INVALID_PILLAR | Invalid Pillar | 400 | No |
E_INVALID_EXTENSION_KEY | Invalid Extension Key | 400 | No |
E_OCCURRED_AT_ON_CHALLENGE | Occurred-at on Challenge | 400 | No |
E_POLICY_BINDING_FAILED | Policy Binding Failed | 400 | No |
E_JWS_EMBEDDED_KEY | JWS Embedded Key Rejected | 400 | No |
E_JWS_CRIT | JWS Critical Header Rejected | 400 | No |
E_JWS_MISSING_KID | JWS Missing Key ID | 400 | No |
E_JWS_KID_TOO_LONG | JWS Key ID Too Long | 400 | No |
E_JWS_B64_FALSE | JWS b64:false Rejected | 400 | No |
E_JWS_ZIP | JWS zip Header Rejected | 400 | No |
E_JWS_WIRE_VERSION_MISMATCH | Wire Version Mismatch | 400 | No |
E_MISSING_PEAC_VERSION | Missing PEAC Version | 400 | No |
Infrastructure errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_JWKS_FETCH_FAILED | JWKS Fetch Failed | 503 | Yes |
E_RATE_LIMITED | Rate Limited | 429 | Yes |
E_CIRCUIT_BREAKER_OPEN | Circuit Breaker Open | 503 | Yes |
E_INTERNAL | Internal Error | 500 | Yes |
Control errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_CONTROL_DENIED | Control Decision Denied | 403 | No |
E_CONTROL_REVIEW_REQUIRED | Review Required | 202 | Yes |
Identity errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_IDENTITY_MISSING | Identity Missing | 401 | No |
E_IDENTITY_INVALID_FORMAT | Identity Invalid Format | 400 | No |
E_IDENTITY_EXPIRED | Identity Expired | 401 | No |
E_IDENTITY_NOT_YET_VALID | Identity Not Yet Valid | 401 | Yes |
E_IDENTITY_SIG_INVALID | Identity Signature Invalid | 401 | No |
E_IDENTITY_KEY_UNKNOWN | Identity Key Unknown | 401 | Yes |
E_IDENTITY_KEY_EXPIRED | Identity Key Expired | 401 | No |
E_IDENTITY_KEY_REVOKED | Identity Key Revoked | 401 | No |
E_IDENTITY_BINDING_MISMATCH | Identity Binding Mismatch | 400 | No |
E_IDENTITY_BINDING_STALE | Identity Binding Stale | 401 | Yes |
E_IDENTITY_BINDING_FUTURE | Identity Binding Future | 400 | No |
E_IDENTITY_PROOF_UNSUPPORTED | Identity Proof Unsupported | 400 | No |
E_IDENTITY_DIRECTORY_UNAVAILABLE | Identity Directory Unavailable | 503 | Yes |
Verifier errors
| Code | Title | HTTP | Retryable |
|---|---|---|---|
E_VERIFY_RECEIPT_TOO_LARGE | Receipt Too Large | 413 | No |
E_VERIFY_MALFORMED_RECEIPT | Malformed Receipt | 400 | No |
E_VERIFY_ISSUER_NOT_ALLOWED | Issuer Not Allowed | 403 | No |
E_VERIFY_KEY_FETCH_BLOCKED | Key Fetch Blocked | 403 | No |
E_VERIFY_KEY_FETCH_FAILED | Key Fetch Failed | 502 | Yes |
note
The verifier category contains additional error codes. The full list is available in the kernel specification.
Using error codes
error-handling.ts
import { PeacError, PEAC_ERRORS } from '@peac/kernel';
import { verifyLocal } from '@peac/protocol';
const result = verifyLocal(receipt, publicKey);
if (!result.valid) {
console.log(result.code); // 'E_INVALID_SIGNATURE'
console.log(result.reason); // Human-readable description
// Programmatic handling by error code
if (result.code === 'E_JWKS_FETCH_FAILED') {
// Retry with backoff: this error is transient
}
}
Error recovery hints
Every error definition includes a next_action field with an agent-actionable recovery hint:
| Next action | Meaning |
|---|---|
retry_after_delay | Retry the same request after a delay |
retry_with_different_key | Retry with a different signing key |
retry_with_different_input | Modify the input and retry |
refresh_attestation | Obtain a fresh identity attestation |
contact_issuer | Contact the receipt issuer for resolution |
abort | Do not retry; the error is permanent |
none | No specific recovery action suggested |
info
All error codes are available as TypeScript constants via @peac/kernel. The normative source of truth is specs/kernel/errors.json: the TypeScript constants are generated from this file, and CI checks for drift.