Error Recovery
Every PEAC error definition includes a next_action hint -- a closed vocabulary of recovery actions that agents can take without human intervention. This enables automated error handling in agent-to-agent workflows.
Recovery vocabulary
| Action | When to use | Agent behavior |
|---|---|---|
retry_after_delay | Transient failure (rate limit, temporary unavailability) | Wait and retry the same operation |
retry_with_different_key | Key not found or key expired | Try a different kid or refresh JWKS |
retry_with_different_input | Input validation failure | Modify the input and retry |
refresh_attestation | Attestation expired or stale | Obtain a fresh attestation and retry |
contact_issuer | Issuer-side configuration error | Escalate to issuer (not automatically recoverable) |
abort | Unrecoverable error (invalid signature, tampered receipt) | Stop processing; do not retry |
none | Informational error (no action needed) | Log and continue |
Using recovery hints
Every error returned by PEAC verification and issuance functions includes the next_action field:
import { verifyReceipt } from '@peac/protocol';
const result = await verifyReceipt(receipt, { issuerUrl });
if (!result.verified) {
for (const failure of result.failures) {
switch (failure.next_action) {
case 'retry_after_delay':
await sleep(failure.retry_after ?? 1000);
// Retry the same verification
break;
case 'retry_with_different_key':
// Refresh JWKS cache and retry
break;
case 'abort':
// Unrecoverable -- log and stop
console.error(`Verification failed: ${failure.message}`);
break;
default:
console.warn(`Unhandled action: ${failure.next_action}`);
}
}
}
Error structure
interface ErrorDefinition {
code: string; // e.g., 'SIGNATURE_INVALID'
message: string; // Human-readable description
category: string; // Error category
retryable: boolean; // Whether retry may succeed
next_action: NextAction; // Agent-actionable recovery hint
retry_after?: number; // Suggested delay in milliseconds (optional)
}
The retryable boolean provides a quick check: if false, the error is deterministic and retrying with the same input will produce the same failure. The next_action provides more specific guidance on what to change before retrying.
Common error patterns
Expired receipt
code: RECEIPT_EXPIRED
retryable: false
next_action: refresh_attestation
The receipt's exp claim has passed. The agent should request a fresh receipt from the issuer.
Key not found
code: KEY_NOT_FOUND
retryable: true
next_action: retry_with_different_key
The kid in the receipt header does not match any key in the issuer's JWKS. The JWKS cache may be stale; refresh and retry.
Invalid signature
code: SIGNATURE_INVALID
retryable: false
next_action: abort
The Ed25519 signature does not verify against the public key. This is unrecoverable -- the receipt has been tampered with or was signed by a different key.
Rate limited
code: RATE_LIMITED
retryable: true
next_action: retry_after_delay
retry_after: 5000
The verification service is rate limiting requests. Wait the suggested delay and retry.
Transport-neutral
Recovery hints are transport-neutral. They work the same way regardless of whether the error occurs during HTTP verification, MCP tool calls, or A2A message processing. The next_action vocabulary is part of the protocol, not tied to any specific transport.