Skip to main content

Errors

The Anchor program defines its errors as a single enum starting at discriminant 6000. The backend translates each program error into a typed HTTP error code; the agent SDK and MCP server preserve those codes verbatim.

DiscriminantProgram errorHTTP statusAPI codeWhat it means
6000WhitelistViolation403whitelist_violationRecipient address has no WhitelistEntry PDA, or the PDA exists but its entry_type doesn't match the operation.
6001WhitelistExpired403whitelist_expirednow > whitelist_entry.ttl_expires_at for a type-1 entry.
6002WhitelistAmountExhausted403whitelist_amount_exhaustedamount_used + amount > approved_amount for a type-1 entry.
6003PerTxLimitExceeded403per_tx_limit_exceededamount > agent_wallet.per_tx_limit.
6004DailyLimitExceeded403daily_limit_exceededrolling_24h_spend + amount > agent_wallet.daily_limit.
6005HourlyFreqExceeded403hourly_freq_exceededrolling_60min_count + 1 > agent_wallet.hourly_tx_cap.
6006NonceMismatch409nonce_mismatchExpected agent_wallet.nonce + 1, got something else. Replay or stale tx.
6007AgentInactive403agent_inactiveAgent's is_active flag is paused or revoked.
6008Unauthorized401unauthorizedThe signer doesn't match the expected authority (owner vs operator vs agent).
6009InvalidMint400invalid_mintToken mint mismatch — the agent's mint doesn't match the token account on the instruction.
6010OperationNotSupported400operation_not_supportedThe whitelist entry's entry_type doesn't permit this operation (e.g., trying to swap with a type-0 entry).
6011InvalidAmount400invalid_amountAmount is zero or exceeds u64::MAX after fee calculation.
6012InsufficientFunds400insufficient_fundsAgent's ATA balance is less than amount.
6013EmergencyMode403emergency_modeGroup is in emergency-withdraw state — no execute_* instructions accepted.

Error envelope

The Agent REST API wraps every error in a consistent envelope:

{
"error": {
"code": "whitelist_amount_exhausted",
"message": "Recipient GjwoT...uzbh consumed its $5.00 whitelist budget on tx 5JxKp...",
"details": {
"recipient": "GjwoT1Hf2Rhet9NEr4D5jgN5VdfpxdmxbbPLLgUrjgyy",
"approved_amount": 5.00,
"amount_used": 5.00,
"auto_voided_at": "2026-05-08T17:23:44Z"
}
},
"trace_id": "01HXYZ..."
}

The code field is the canonical machine-readable identifier — match on this, not on the message. The message is human-readable and may change between releases.

Distinguishing causes

When a transfer fails, the order of checks in the program is:

  1. PDA seed (existence) — whitelist_violation
  2. TTL — whitelist_expired
  3. Amount cap on whitelist — whitelist_amount_exhausted
  4. Per-tx cap — per_tx_limit_exceeded
  5. Daily cap — daily_limit_exceeded
  6. Hourly count — hourly_freq_exceeded
  7. Nonce — nonce_mismatch
  8. Token transfer (failure here means insufficient funds or other SPL error) — insufficient_funds

So if your transfer returns whitelist_violation, you know the recipient isn't whitelisted at all — it's not a TTL or amount issue. If it returns whitelist_expired, the entry exists but is past its TTL. And so on.

Recoverable vs unrecoverable

ErrorRecoverable?How
whitelist_violationYesOrchestrator adds the recipient via add_to_whitelist.
whitelist_expiredYesOrchestrator renews via renew_whitelist_entry.
whitelist_amount_exhaustedYesOrchestrator tops up via renew_whitelist_entry or recreates the entry.
per_tx_limit_exceededSometimesEither split into smaller chunks, or the orchestrator raises per_tx_limit.
daily_limit_exceededWaitCounter rolls over after 24h. Orchestrator can also raise the cap.
hourly_freq_exceededWaitCounter rolls over within 60 minutes.
nonce_mismatchAutoBackend's resolveNonceMismatch retries with the chain-fresh nonce. Usually transparent to the agent.
agent_inactiveYesOrchestrator un-pauses the agent.
insufficient_fundsYesTop up the agent's wallet from the orchestrator (or another agent).

For the recoverable cases, agents typically respond by either retrying after a delay (for time-based caps) or messaging the orchestrator to take action (for whitelist or limit cases).

Webhook events for blocked transfers

Every blocked transfer fires the policy.transfer_blocked webhook with the structured error attached. Most orchestrators wire these to a channel they monitor — the goal is to spot patterns (a particular agent hitting whitelist_violation repeatedly often means a misconfigured prompt or a compromised key).

See Webhooks for full payload shapes.