Reason codes — global registry
Every bot output carries a reason_code. Codes are stable across versions, machine-readable, and dashboard-filterable. The registry is hybrid: a small set of global severity codes that every system must understand, plus per-bot specifics scoped within those severities.
The five severities
| Severity | Meaning | Behaviour | Where it lives |
|---|---|---|---|
| HARD_REJECT | The order or action must not proceed. | Pipeline aborts; user is shown a plain-English explanation; nothing is signed or submitted. | Risk & Security layers. |
| RESHAPE | The order may proceed only after reshape constraints are applied. | Risk emits constraints (size cap, price cap, passive_only, close_only); Execution applies them. | Risk layer. |
| WARN | Concern raised; order proceeds. | Logged, surfaced on the post-trade explanation; does not block. | Any layer. |
| EXPLAIN | Informational annotation for post-trade reporting. | Attached to the PostTradeExplanation; never shown as an alert. | Governance layer. |
| INFO | Routine telemetry. | Logs only. | Any layer. |
Naming rules
UPPER_SNAKE_CASE. Always.- Stable across versions — never reword an existing code; deprecate and add a new one.
- Specific enough to drive a dashboard filter; never an English sentence.
- The first word is the subject, not the verb.
ORDER_BOOK_UNAVAILABLE, notUNAVAILABLE_ORDER_BOOK. - One reason code per outcome. If a bot has three reasons to reject, it has three codes.
Global codes — every system must understand these
| Code | Severity | When emitted |
|---|---|---|
KILL_SWITCH_ACTIVE | HARD_REJECT | Global kill switch is engaged. |
STALE_MARKET_DATA | HARD_REJECT | Required market data older than its staleness threshold. |
WALLET_PERMISSION_DENIED | HARD_REJECT | User wallet refused the action or session expired. |
CONTRACT_ADDRESS_NOT_ALLOWED | HARD_REJECT | Target contract is not on the V2 allow-list. |
ORACLE_DISPUTE_ACTIVE | HARD_REJECT | UMA Optimistic Oracle proposal is under dispute. |
PARAMETER_CHANGE_REQUIRES_APPROVAL | HARD_REJECT | Strategy attempted a mid-run parameter change without re-validation. |
INSUFFICIENT_VISIBLE_DEPTH | RESHAPE | Order would consume too much of visible top-of-book. |
SPREAD_TOO_WIDE | RESHAPE | Current spread exceeds the warning multiple of 30d median. |
STRATEGY_BUDGET_EXCEEDED | RESHAPE | Order would push strategy spend over its declared budget. |
NEGRISK_CONVERT_AVAILABLE | WARN | Sum of YES prices in a negative-risk set is below 1; arbitrage possible via NegRiskAdapter. |
FEE_RATE_CAPPED | WARN | Builder fee rate clamped to 100 bps taker / 50 bps maker. |
RECONCILIATION_DRIFT_OBSERVED | EXPLAIN | Internal view of a fill diverged from on-chain settlement and was reconciled. |
Per-bot scoping
Each bot may add its own codes inside one of the five severities. Convention: prefix with the bot's namespace.
- LiquidityGuard:
LIQUIDITY_GUARD_BOOK_UNAVAILABLE - OracleRiskMonitor:
ORACLE_PROPOSER_BOND_BELOW_MIN(UMA $750 pUSD bond) - SmartRouter:
SMART_ROUTER_TICK_SIZE_CHANGED - BuilderAttribution:
BUILDER_CODE_MISSING
Per-bot codes are listed in the Reason codes section of each bot's page.
Deprecation
To retire a code: add the new code, mark the old one as deprecated in the bot's Versioning & Migration block, run both for at least 30 days, then remove emission. Never remove a code from the registry — keep it as DEPRECATED with a pointer to the replacement so historical logs remain decodable.