Error codes
Every failure carries a stable envelope:
{ "code": "...", "message": "...", "hint": "what to do about it", "location": "file:line:col or null" }
Codes are part of the compatibility promise — minor releases never rename them.
Framework envelopes (EM_*)
| Code | Raised by |
|---|---|
EM_UNKNOWN |
base class fallback (a bug if you ever see it — please report) |
EM_CANON |
RFC 8785 canonicalization failure (non-JSON-able value) |
EM_DIGEST |
digest chaining misuse |
EM_PRNG |
keyed PRNG misuse (unknown domain, bad snapshot) |
EM_CLOCK |
virtual-clock misuse (bad ISO time, backwards advance) |
EM_GUARD |
determinism guard violation (socket / wall clock inside a run) |
EM_STORE |
world-store failure (missing entity on delete, snapshot IO) |
EM_SCENARIO |
scenario load/validation error (with YAML line:col) |
EM_ASSERT_SYNTAX |
CEL expression does not parse |
EM_ASSERT_EVAL |
CEL evaluation failed or returned a non-boolean |
EM_TOOL |
default for business tool errors (usually overridden below) |
EM_RUN |
runner lifecycle misuse (calling into a finished run) |
Business tool codes
| Code | Meaning |
|---|---|
ARGS_INVALID |
missing/unknown/mistyped arguments (schema-checked) |
TOOL_NOT_ENABLED |
tool not in this scenario's tools: (journaled) |
BUDGET_EXCEEDED |
max_steps / max_duration crossed → verdict budget_exceeded, exit 3 |
TURN_NOT_READY |
eval.next_turn pulled before its when: is true (journaled) |
EMAIL_NOT_FOUND |
email pack: unknown message id |
CONTACT_NOT_FOUND |
crm pack: unknown or deleted contact |
EVENT_NOT_FOUND |
calendar pack: unknown event id |
TIME_CONFLICT |
calendar pack: overlapping [start, end) windows |
ENTITY_NOT_FOUND |
BYO packs: unknown entity id |
MALFORMED_TOOL_CALL |
adapter: the model's tool_call arguments were not a JSON object (nothing executed, not journaled) |
Injected fault codes (faults:)
| Code | Fault |
|---|---|
TIMEOUT |
error: timeout |
RATE_LIMIT |
error: rate_limit |
SERVER_ERROR |
error: server_error |
PERMISSION_DENIED |
error: permission_denied |
| — | error: malformed returns a corrupted result instead of an error (that's the trap) |
All injected calls carry fault_injected: true in the journal.
Lint warnings (eval-mock lint)
| Code | Smell |
|---|---|
W001 |
assertion indexes entities by position ([0]) |
W002 |
assertion references a tool that is not enabled |
W003 |
no collateral allowlist assertion |
W004 |
faults injected but no assertion checks fault/retry behaviour |
Warnings never block unless --strict.
Verify / migrate outcomes
eval-mock verify: VERIFIED · SCENARIO_CHANGED · ENGINE_MISMATCH ·
PACK_CHANGED · TRAJECTORY_DIVERGED (with the first divergent journal
seq). eval-mock migrate: CURRENT · MIGRATED.