Errors and operations
Every decision carries a stable, machine-readable reason code. Codes are append-only and never renumbered.
Reason codes
| Code | Meaning | Retry |
|---|---|---|
| RW_AUTH_001 | Authentication failed | no |
| RW_POLICY_001 | No valid active policy | no |
| RW_POLICY_002 | Explicit deny rule matched | no |
| RW_TARGET_001 | Target contract not allowed | no |
| RW_METHOD_001 | Function selector not allowed | no |
| RW_BUDGET_001 | Budget limit exceeded | after condition clears |
| RW_RATE_001 | Rate limit exceeded | after condition clears |
| RW_LOOP_001 | Repeated intent or calldata detected | no |
| RW_REPLAY_001 | Duplicate or expired authorization | no |
| RW_ATTEST_001 | Enclave attestation invalid | after condition clears |
| RW_SIM_001 | Simulation failed | no |
| RW_FEE_001 | Fee estimation unavailable or invalid | after condition clears |
| RW_HUMAN_001 | Human approval required | no |
| RW_PAUSED_001 | Agent or account is paused | after condition clears |
| RW_STATE_001 | Authoritative state unavailable | after condition clears |
| RW_CHAIN_001 | Chain or bundler unavailable | after condition clears |
| RW_REQUEST_001 | Request failed schema validation | no |
RW_REQUEST_001 extends the original specification table: request schema failures need a code distinct from policy failures.
A denial is not a revert
When the gateway denies an operation before broadcast, it returns a structured JSON-RPC error. No transaction exists, so there is no transaction hash and no revert. An EVM revert happens only when an executed transaction reverts on chain.
JSON-RPC denial · proposed error shape◆ pseudocode · not yet runnable
{
"error": {
"code": -32090,
"message": "Ravelin Wall denied the operation",
"data": {
"reason_code": "RW_BUDGET_001",
"request_id": "rw_req_example",
"retryable": true,
"transaction_hash": null
}
}
}Retry semantics
- Retry only operations created with an idempotency key. A retry with the same key returns the original result.
- Use bounded exponential backoff. Never retry a financial action without proof that it cannot execute twice.
RW_LOOP_001is not retryable: change the intent, or wait out the cooldown with a new request.
Operation states
Approval, signing, submission, inclusion and success are separate states, never conflated.
| State | Meaning |
|---|---|
| evaluated | Policy decision recorded. Nothing signed. |
| pending_approval | Awaiting an independent human signature over this exact operation. |
| authorized | Authorization bound to op hash, chain, policy version and expiry. |
| signed | Signed by the protected signer. Not yet submitted. |
| submitted | Accepted by the bundler. Reservation still held. |
| included | In a block. Awaiting finality; reorgs are reconciled. |
| succeeded / reverted | Final execution outcome. Reservation finalized with actual fee. |
| denied / expired / rejected | Terminal. Reservation released. No transaction hash. |
Failure behaviour
| Failure | Required behaviour |
|---|---|
| Policy unavailable | Do not authorize new signing. |
| State store unavailable | Do not approve on stale counters (RW_STATE_001). |
| Enclave unavailable | Typed unavailable error. Never fall back to an unprotected signer. |
| Fee estimation unavailable | Deny or require review per policy (RW_FEE_001). |
| Bundler unavailable | Preserve operation state and reservation until reconciled (RW_CHAIN_001). |
| Telemetry unavailable | Buffer bounded, redacted events. Decisions are unaffected. |
| RPC disagreement | Do not finalize on an unverified receipt. |
| Approval expires | Invalidate the pending authorization. |
| Duplicate request | Return the existing idempotent result, or RW_REPLAY_001. |
Recovery procedures
- Denied (terminal). Read
reason_codeandmessage. Change the intent or the policy; never retry the same request. Nothing was signed, so there is nothing to reconcile. - Retryable denial. If
retryableis true (budget, rate, fee, state, chain), wait for the condition to clear and resubmit with the same idempotency key. A repeat with the same key returns the original result. - Pending approval. Poll the approval or subscribe to its status. If it expires or the policy version changes, the pending authorization is invalidated and its reservation released; submit a new request.
- Submitted, outcome unknown. Do not resubmit. Query the operation until it reaches a final state; the reservation is held until the receipt is reconciled, so a retry cannot create a second execution.
- Emergency stop. New operations are denied with
RW_PAUSED_001. Resume only after the stop is released by an authorized operator and health checks pass.
RPC and bundler outages
When the RPC endpoint, fee estimator or bundler is unavailable, new authorizations fail closed (RW_FEE_001, RW_CHAIN_001). Already-submitted operations keep their state and reservation until a receipt is verified; an unverified or disputed receipt never finalizes spend. Retries use bounded exponential backoff and the original idempotency key.
Debugging
- Every response carries a
request_id. Use it to find the audit record, which lists every check in evaluation order with its result and detail. - Reproduce a decision locally: the reference evaluator is deterministic, so the same policy, request and state give the same result. The workbench and the console simulation run it in the browser.
- Check the policy version in the response against the version you expect to be active. Approvals and authorizations are bound to the version they were evaluated under.
transaction_hashis null whenever nothing was broadcast. A non-null hash with a failed status is an EVM revert, which is different from a denial.