Skip to main content
Version: v0.16.1

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"
}
FieldDescription
typeCanonical URL for the error type
titleHuman-readable error title
statusHTTP status code
detailHuman-readable explanation of the specific error
instanceURI reference identifying the specific occurrence
peac_error_codeStable PEAC error code for programmatic handling

Categories

CategoryDescription
verificationSignature and key verification
validationFormat, claim, and field validation
infrastructureJWKS fetch, rate limiting, circuit breaker
controlPolicy engine authorization decisions
identityAgent identity attestation
attributionSource attribution and content hashing
bundleEvidence bundle creation and validation
disputeDispute evidence and resolution
interactionInteraction evidence schema
workflowWorkflow DAG and step validation
verifierVerifier-side checks and policy

Verification errors

CodeTitleHTTPRetryable
E_INVALID_SIGNATUREInvalid Signature400No
E_KEY_NOT_FOUNDKey Not Found400No

Validation errors

CodeTitleHTTPRetryable
E_INVALID_FORMATInvalid Format400No
E_EXPIREDReceipt Expired400No
E_NOT_YET_VALIDNot Yet Valid400Yes
E_INVALID_ISSUERInvalid Issuer400No
E_INVALID_AUDIENCEInvalid Audience400No
E_INVALID_AMOUNTInvalid Amount400No
E_INVALID_CURRENCYInvalid Currency400No
E_INVALID_RAILInvalid Payment Rail400No
E_MISSING_REQUIRED_CLAIMMissing Required Claim400No
E_EVIDENCE_NOT_JSONEvidence Not JSON-Safe400No
E_INVALID_SUBJECTInvalid Subject400No
E_INVALID_RECEIPT_IDInvalid Receipt ID400No
E_MISSING_EXPMissing Expiration400No

Interaction Record format errors

The Interaction Record format introduces additional error codes for structured validation:

CodeTitleHTTPRetryable
E_ISS_NOT_CANONICALIssuer Not Canonical400No
E_INVALID_KINDInvalid Kind400No
E_INVALID_TYPEInvalid Type400No
E_INVALID_PILLAR_VALUEInvalid Pillar Value400No
E_PILLARS_NOT_SORTEDPillars Not Sorted400No
E_INTERACTION_INVALID_EXTENSION_KEYInteraction Invalid Extension Key400No
E_EXTENSION_GROUP_REQUIREDExtension Group Required400No
E_EXTENSION_GROUP_MISMATCHExtension Group Mismatch400No
E_OCCURRED_AT_ON_CHALLENGEoccurred_at on Challenge400No
E_OCCURRED_AT_FUTUREoccurred_at in Future400No
E_POLICY_BINDING_FAILEDPolicy Binding Failed400No
E_JWS_EMBEDDED_KEYJWS Embedded Key Rejected400No
E_JWS_CRIT_REJECTEDJWS crit Header Rejected400No
E_JWS_MISSING_KIDJWS kid Missing or Invalid400No
E_JWS_B64_REJECTEDJWS b64:false Rejected400No
E_JWS_ZIP_REJECTEDJWS zip Header Rejected400No
E_UNSUPPORTED_WIRE_VERSIONUnsupported Wire Version400No
E_WIRE_VERSION_MISMATCHWire Version Mismatch400No

I-JSON input validation (v0.16.0+)

The JWS protected header and payload are validated as I-JSON (RFC 7493) before parsing. Non-conforming input is rejected with these codes:

CodeTitleHTTPRetryable
E_IJSON_DUPLICATE_MEMBER_NAMEDuplicate JSON Member Name400No
E_IJSON_NUMBER_OUT_OF_RANGENumber Out Of I-JSON Range400No
E_IJSON_INVALID_STRINGInvalid JSON String400No

E_IJSON_NUMBER_OUT_OF_RANGE covers non-finite numbers and numbers whose magnitude exceeds the safe-integer range; encode large values as strings. E_IJSON_INVALID_STRING covers invalid escapes, lone surrogates, Unicode noncharacters, and invalid UTF-8.

Infrastructure errors

CodeTitleHTTPRetryable
E_JWKS_FETCH_FAILEDJWKS Fetch Failed503Yes
E_RATE_LIMITEDRate Limited429Yes
E_CIRCUIT_BREAKER_OPENCircuit Breaker Open503Yes
E_INTERNALInternal Error500Yes

Control errors

CodeTitleHTTPRetryable
E_CONTROL_DENIEDControl Decision Denied403No
E_CONTROL_REVIEW_REQUIREDReview Required202Yes

Identity errors

CodeTitleHTTPRetryable
E_IDENTITY_MISSINGIdentity Missing401No
E_IDENTITY_INVALID_FORMATIdentity Invalid Format400No
E_IDENTITY_EXPIREDIdentity Expired401No
E_IDENTITY_NOT_YET_VALIDIdentity Not Yet Valid401Yes
E_IDENTITY_SIG_INVALIDIdentity Signature Invalid401No
E_IDENTITY_KEY_UNKNOWNIdentity Key Unknown401Yes
E_IDENTITY_KEY_EXPIREDIdentity Key Expired401No
E_IDENTITY_KEY_REVOKEDIdentity Key Revoked401No
E_IDENTITY_BINDING_MISMATCHIdentity Binding Mismatch400No
E_IDENTITY_BINDING_STALEIdentity Binding Stale401Yes
E_IDENTITY_BINDING_FUTUREIdentity Binding Future400No
E_IDENTITY_PROOF_UNSUPPORTEDIdentity Proof Unsupported400No
E_IDENTITY_DIRECTORY_UNAVAILABLEIdentity Directory Unavailable503Yes

Verifier errors

CodeTitleHTTPRetryable
E_VERIFY_RECEIPT_TOO_LARGEReceipt Too Large413No
E_VERIFY_MALFORMED_RECEIPTMalformed Receipt400No
E_VERIFY_ISSUER_NOT_ALLOWEDIssuer Not Allowed403No
E_VERIFY_KEY_FETCH_BLOCKEDKey Fetch Blocked403No
E_VERIFY_KEY_FETCH_FAILEDKey Fetch Failed502Yes
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 actionMeaning
retry_after_delayRetry the same request after a delay
retry_with_different_keyRetry with a different signing key
retry_with_different_inputModify the input and retry
refresh_attestationObtain a fresh identity attestation
contact_issuerContact the receipt issuer for resolution
abortDo not retry; the error is permanent
noneNo 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.