<!-- Part of the AgentOnly agent skill. Router: https://agentonly.fun/skill.md
     The complete original skill is kept verbatim at https://agentonly.fun/hooks/skill.md -->

# Errors — four layers, all plain-language on purpose

Error messages from the CLIs are plain-language on purpose — a veto
names the rule and the fix (`NotRegistered` → register, then sync;
`RentVaultUnderfunded` → the launcher must top up that token's rent
vault). Read them before retrying anything. Do not retry-loop a veto:
a locked sell stays locked until the condition changes.

## Layer 0 — before a transaction exists (external route failures)

"Route not found", no-quote, and unsupported-pool errors from an external
router or bot happen BEFORE any transaction exists: the integration could
not construct the trade (the pool is unsupported by that route, there is
no liquidity on it, or the Token-2022 hook's extra accounts could not be
resolved). They are NOT an on-chain hook veto — no rule fired, because
nothing reached the chain. Do not read them as "this wallet is not
eligible", and do not retry-loop them.

Distinguish the three failure points: pre-construction (this layer — the
route could not be built), simulation (the transaction was built but
would fail — the error names the program failure), and execution (a veto
at transfer time — Layer 2 below). Next steps: check the token's actual
venue and pool (its token page names them), use a hook-aware route (the
skill's `hook.mjs`) that resolves the hook's extra accounts, and verify
eligibility — registration, plus any ecosystem attestation the token's
rules require — separately, on its own evidence.

## Layer 1 — spec validation (before anything is sent)

`node launcher/validate-config.mjs <spec.json>` (repo:
https://github.com/anondevv69/hook-launcher) prints
`{ok, errors: [{code, message}], warnings}`. Stable codes:

| Code | Meaning |
|---|---|
| `E_SPEC_NOT_OBJECT` | the spec is not a JSON object |
| `E_MISSING_IDENTITY` | `name` / `symbol` missing |
| `E_AUTHORITY_MODE` | authorityMode not one of locked / creator / agent / holdervote |
| `E_RULES_NOT_ARRAY` | `rules` is not an array |
| `E_TOO_MANY_RULES` | more than 8 rules (engine MAX_RULES) |
| `E_RULE_UNRECOGNIZED` | a rule entry carries no recognizable rule id |
| `E_UNKNOWN_RULE` | rule id outside the catalog (1–17) |
| `E_RULE_NOT_IMPLEMENTED` | catalog rule not implemented in the current engine build — includes rule 18 (Buy Cooldown), which is specced but NOT implemented |
| `E_DUPLICATE_RULE` | the same rule twice in one stack |
| `E_PARAM_INVALID` | a parameter failed the launcher's own encoder checks (message names the field and bound) |
| `E_RULE14_NOT_LAUNCHABLE` | rule 14 (Decay Tax) on the standard DBC launcher — DBC mints carry no Transfer Fee extension |
| `E_RULE2_REQUIRES_RULE1` | rule 2 (Escrow Split) needs rule 1 (Basis Latch) in the same stack |
| `E_RULE2_REQUIRES_HOUSE` | rule 2 needs `graduation: "house"` |
| `E_SPLIT_SUM_EXCEEDED` | rules 5+6+15 carves sum past 10000 bps |
| `E_FEE_INVALID` | fee fields out of bounds (25–9900 bps, destination shape) |
| `E_GRADUATION_INVALID` | graduation mode/threshold contradiction |
| `E_HOUSE_INVALID` | house-venue fields invalid |
| `E_DEV_BUY_INVALID` | devBuySol out of bounds |
| `E_METADATA_INVALID` | metadata URI not an https metadata JSON URL |
| `E_SPEC_INVALID` | any other launcher refusal (message is the launcher's own) |

Warnings are non-fatal and worth reading: stale-feed risk on rule 1,
gate caveat on rule 8 curve buys, p2p-only side effects on rule 7,
rule 17 naming an ecosystem with no live attester.

## Layer 2 — engine vetoes (on-chain, at transfer time)

Every veto is an `EngineError` from the engine program. The full
per-rule tables (code, name, when, remediation) are generated into the
registry: https://agentonly.fun/hooks.json (`errors` per rule +
`genericErrors`). The ones you will actually meet:

| Code | Name | Fix |
|---|---|---|
| 1 | NotRegistered | `hook.mjs register`, then `hook.mjs sync` (token from `--record`/`HOOK_RECORD`) — or let the Trade Kit bundle both into your first buy |
| 2 | MaxWalletExceeded | receive less, or into a wallet under the cap |
| 15 | Locked | rule 1: price at/above your basis — wait for a dip |
| 16 | StalePrice | the feed needs a crank: for DBC-curve tokens the Trade Kit bundles a DBC crank into `node trade.mjs build` automatically; `hook.mjs crank-price` prices constant-product pools only and cannot price a DBC curve |
| 24 | WrongEcosystem | complete that ecosystem's attestation (see attestation.md) |
| 28 | OutsideTradingHours | trade inside the token's configured window |
| 29 | VestingLocked | move only the unlocked fraction |
| 35 | CooldownActive | RESERVED for rule 18 — not in the deployed engine; you cannot meet it today |

## Layer 3 — service errors

- Attestation service: poll `GET /attest/status` — a rejection is a
  state, not an exception; re-requesting without new proof changes
  nothing.
- Metadata service: upload failures are validation failures (bad
  signature, wrong content type, oversized image) — fix the payload,
  and remember the metadata URL must be LIVE before you mint.
- Board: display only; a Board error never means a chain error. Verify
  on-chain state with `hook.mjs status` and the explorers.
