Error Code Reference
Every error code the Vault can return, what it means, and what to do about it.
How errors look
Every error response from the Vault is JSON with a human-readable
error message and a machine-readable code:
{
"status": "error",
"error": "Would exceed daily limit",
"code": "EXCEEDS_DAILY_LIMIT"
}
The HTTP status of the response tells you the kind of failure; the code tells you the exact cause. As a rule of thumb:
| HTTP status | What it means | What to do |
|---|---|---|
| 400 | The request itself is wrong | Fix the request before retrying |
| 401 | Authentication failed | Check credentials and signature |
| 403 | Understood, but refused | Retrying unchanged will not help |
| 404 | The thing referenced does not exist | Check the identifier |
| 409 | Conflicts with current state | Start over with a fresh request |
| 429 | A limit that clears on its own | Back off and retry later |
| 500 | Something failed on the Vault's side | The request was fine; retrying is reasonable |
| 503 | Temporarily unavailable | Often user-fixable in the app (unlock wallet, reconnect device); retry after |
Agent API
Codes returned by the agent-facing API (default port 4663): the
/sign, /trade, /callback,
/mandate and status endpoints.
400 — Malformed request
| Code | Meaning |
|---|---|
INVALID_JSON | The request body is not valid JSON |
INVALID_CONTENT_LENGTH | The Content-Length header is missing or invalid |
MISSING_AGENT_ID | No agent_id in the request |
MISSING_SIGNATURE | No signature in the request |
MISSING_TRADE | /trade called without a trade object |
MISSING_REQUEST_ID / INVALID_REQUEST_ID | Status poll without a request id, or with a malformed one |
MISSING_TRANSACTION_ID / INVALID_TRANSACTION_ID | Transaction lookup without a transaction id, or with a malformed one |
MISSING_EVENT / INVALID_EVENT | /callback without an event, or with one that is not a recognized value |
MISSING_TX_HASH / INVALID_TX_HASH | Settlement callback without an on-chain transaction hash, or with a malformed one |
MISSING_X402_DATA | Payment request without the x402 payment data |
INVALID_X402_DATA / INVALID_X402_FORMAT | The x402 payment data does not parse or is not in a supported format |
INVALID_X402_RESPONSE / INVALID_RESPONSE_STATUS | Reserved for x402 response validation (not currently emitted) |
INVALID_PAYMENT_REQUIRED | The 402 Payment Required data forwarded by the agent is invalid |
INVALID_PAYMENT_DATA | Payment fields fail validation |
FIELD_NOT_PERMITTED | The trade includes a field the agent may not set (agent_id, wallet_address, recipient) — the Vault takes these from the authenticated credentials |
BAD_REQUEST | The trade object fails shape validation |
INVALID_REQUEST | Generic malformed request |
ERROR | Generic fallback when no more specific code applies |
401 — Authentication failed
AUTH_FAILED | The bearer token or HMAC signature did not verify. Check the token, the signed message shape, and clock skew (HMAC timestamps have a replay window). For an HMAC agent, a locked wallet also answers AUTH_FAILED rather than WALLET_LOCKED — the shared secret needed to check the signature is itself encrypted, so Vault cannot tell "wrong signature" from "wallet locked" without unlocking first for a caller it has not yet verified, and revealing that distinction would let anyone poll for whether the wallet happens to be open. Bearer agents, whose token is checked before the wallet is touched, get the honest WALLET_LOCKED code instead (see below). If every request from an otherwise-correctly-configured HMAC agent fails, check whether the wallet is unlocked in the app. |
403 — Refused by policy or configuration
Retrying the same request will not help; either the request must change or the user must change the policy.
| Code | Meaning |
|---|---|
UNAUTHORIZED | The agent is not allowed to access this resource (e.g. another agent's transaction) |
AGENT_SUSPENDED | The user has suspended this agent |
AGENT_NOT_COMMISSIONED | The agent is registered but not commissioned with a policy and address |
ADDRESS_NOT_COMMISSIONED | The wallet address the agent is tied to is no longer commissioned |
NETWORK_DISABLED | The requested chain is disabled in Vault settings |
NETWORK_NOT_ALLOWED_BY_POLICY | The requested chain is excluded by the agent's policy |
X402_DISABLED | x402 payments are switched off in the agent's policy |
DOMAIN_NOT_ALLOWED | The payee's domain is not on the policy's allowlist |
DOMAIN_URL_REQUIRED | The policy restricts payees by domain, but the request carried no URL to check |
UNSUPPORTED_ASSET | The payment asset is not one the Vault supports |
EXCEEDS_PER_REQUEST_MAX | The amount is over the policy's per-request maximum. Permanent for this request — split it into smaller ones |
POLICY_REJECTED | A trade was refused by the policy at approval or execution time |
DEFI_DISABLED / DEFI_NOT_CONFIGURED | Morpho lending is switched off, or not configured, in the agent's policy |
VENUE_NOT_PERMITTED | The named vault or market is not one the policy's trusted curator stands behind (or the curator changed since the request was made) |
EXPOSURE_EXCEEDED | The deposit would put more at risk than the policy's exposure limit allows |
EXPOSURE_UNREADABLE | Current on-chain exposure could not be read, so it cannot be confirmed within limits; treated as a refusal rather than assumed safe |
LEDGER_REJECTED | The user declined the signature on their hardware wallet |
FOREIGN_HOST_REJECTED | The request came from another machine; the API serves localhost only (unless --allow-lan) |
BROWSER_ORIGIN_REJECTED | The request came from a web page; browser calls are refused |
404 — Not found
AGENT_NOT_FOUND / UNKNOWN_AGENT | No agent with that id (it may have been deleted) |
POLICY_NOT_FOUND | The agent's policy no longer exists |
AGENT_OR_POLICY_MISSING | The agent or its policy has been removed since the request was made |
TRANSACTION_NOT_FOUND | No transaction with that id |
REQUEST_NOT_FOUND | No pending request with that id. Note that payment, trade and lending requests live on different status endpoints; polling the wrong one returns this. While a trade or lending request is actually being approved, its status reads executing rather than 404, so this code means the id genuinely does not exist |
ADDRESS_NOT_FOUND | The referenced wallet address does not exist |
NOT_FOUND | The URL path does not match any endpoint, or the resource does not exist |
405 / 413 — Transport-level rejections
METHOD_NOT_ALLOWED | Wrong HTTP method for this endpoint (the Allow header lists valid ones) |
PAYLOAD_TOO_LARGE | Request body over the 1 MB limit |
409 — Conflicts with current state
The request was fine when made, but the world has moved. Start over with a fresh request rather than retrying this one.
REQUEST_ALREADY_PROCESSED | This approval request was already approved or rejected |
PAYMENT_ALREADY_SETTLED | This payment already settled on-chain; use an idempotency_key for a fresh payment |
REQUEST_EXPIRED | The approval window closed before the user acted; submit a new request |
PRICE_MOVED | The market moved past the slippage tolerance while the trade awaited approval; re-quote and resubmit |
TRADE_NO_LONGER_VALID | The trade could not be revalidated at approval time |
429 — Limits that clear on their own
Nothing about the request is wrong; waiting is the remedy.
RATE_LIMIT_EXCEEDED | Too many requests in a short window |
EXCEEDS_DAILY_LIMIT / LIMIT_REACHED | The policy's daily spending limit is used up; it renews at local midnight, once at least 20 hours have passed since the last renewal |
TOO_MANY_PENDING | Too many requests from this agent already await user approval |
500 — Failed on the Vault's side
The request was acceptable; something went wrong executing it. Resending unchanged is reasonable.
SIGNING_ERROR | Signing failed unexpectedly |
EXECUTION_ERROR | A trade or lending operation failed during on-chain execution |
REQUOTE_FAILED | The Vault could not re-quote the pool or venue to validate the request |
INSUFFICIENT_LIQUIDITY | A Morpho withdrawal asked for more than the venue can currently free. Retryable — the venue may be able to free more once other borrowers repay |
APPROVAL_REVERTED / REVERTED | The ERC-20 approval, or the supply/withdraw itself, reverted on-chain |
RPC_REJECTED | The node refused the transaction before ever broadcasting it (insufficient gas, a stale nonce). Nothing was sent |
NO_WALLET_PROVIDER / NO_WALLET | No wallet is loaded in the Vault |
NO_WALLET_ADDRESS | The wallet has no address to trade or sign from |
SDK_NOT_FOUND | A required signing dependency is not installed |
LEDGER_ERROR | The hardware wallet returned an unexpected error |
LEDGER_NO_SIGNATURE | The hardware wallet returned no signature |
INTERNAL_ERROR | Unexpected internal failure; details are in the Vault log |
503 — Temporarily unavailable
Usually fixable by the user in the Vault app; retry once fixed.
SERVICE_NOT_READY | The Vault is starting up or the service is not running |
WALLET_LOCKED | The wallet is locked; the user must unlock it in the app |
WALLET_ADDRESS_NOT_FOUND | The agent's address is not in the currently unlocked wallet; the user likely has the wrong wallet open |
LEDGER_DISCONNECTED | The hardware wallet is unplugged or asleep |
LEDGER_SIGN_NOT_AVAILABLE | Nothing on this Vault instance is registered to prompt for a hardware confirmation. Both editions support Ledger signing normally; this fires only in an unusual composition where no prompt handler was wired up |
Codes on successful responses
Two codes ride along on non-error responses to add detail:
APPROVAL_REQUIRED (202) | Accompanies status pending: the user must approve in the app; poll the status endpoint |
ALREADY_SIGNED (200) | Accompanies status success: the payment was signed earlier (the header is no longer cached) |
Command Errors
Codes specific to running a command — Vault Terminal, or Desktop's File → Console panel. These never reach an agent; they are what you see typing at a prompt, not what the HTTP API returns.
| Code | Meaning |
|---|---|
UNLOCK_FAILED | Wrong wallet password |
TOO_MANY_ATTEMPTS | Too many failed unlock attempts; wait out the cooldown |
MISSING_NAME / MISSING_FIELDS | A required field was not provided |
UNKNOWN_SETTING | No setting by that name |
NOT_COMMISSIONED | The operation needs a commissioned agent |
APPROVAL_FAILED | Approving or rejecting the pending request failed |
TRADE_FAILED | The approved trade failed to execute |
CREATE_FAILED / IMPORT_FAILED / DELETE_FAILED / DERIVE_FAILED / RENAME_FAILED | The wallet, seed, or address operation failed; the message says why |
UPDATE_FAILED | Editing the agent failed |
MANDATE_FAILED / SET_MANDATE_FAILED / UPLOAD_FAILED | Generating, setting, or uploading the agent's mandate failed |
WALLET_LOCKED | The operation needs the wallet unlocked |
Client-side codes
The bundled Python client (CoreClient) raises errors
carrying a code. Two are synthesized by the client itself and never sent
by the server:
HTTP_ERROR | The server returned an error the client could not parse as JSON |
ERROR | Default when no code is available |
The authoritative machine-readable list is
ERROR_CODE_TO_HTTP_STATUS in
src/primer_vault/services/server.py in the Vault repository.
If a code you encounter is not on this page, that map is the place to
check first.