err() and raiseError() callsite is lint-checked against it, and the codes are append-only post-v1.0. Wire ralphy into a CI pipeline by matching on the code field; never grep the message (it’s templated and may carry a {detail} that changes between runs).
Shape
Every error writes a JSON payload on stderr:class (see below).
Exit-code class mapping
| Class | Exit | Meaning |
|---|---|---|
user | 2 | Bad flag, missing arg, wrong id, malformed input |
provider | 3 | OpenRouter / ElevenLabs / yt-dlp HTTP failure, rate limit, budget |
env | 4 | Missing API key, missing dep on PATH, fs permission |
gate | 5 | Quality gate or ref-required refusal |
runtime | 1 | E_INTERNAL, uncaught exception |
cancelled | 130 | SIGINT |
classifyExitCode() in cli/lib/errors/catalog.ts. The contract is append-only: classes can’t be renamed, only added.
The catalog
Codes group by class. Every row carries a message template (with{placeholder} tokens interpolated at raise time) and a remediation hint that names a verb / file / doc — never paraphrasing the message.
User errors (exit 2)
| Code | Message template | Hint |
|---|---|---|
E_INPUT_INVALID | Invalid input for <field>: <detail> | Check the flag value against ralphy <verb> --help and retry. |
E_FLAG_MISSING | Missing required flag: —<flag> | Run ralphy <verb> --help to see required flags. |
E_FLAG_UNKNOWN | Unknown flag value for —<flag>: <value> | Allowed values: <allowed>. See ralphy <verb> --help. |
E_NOT_FOUND | <kind> not found: <id> | Run ralphy <kind> list to see available ids. |
E_ALREADY_EXISTS | <kind> already exists: <id> | Pick a different id with --as <id> or delete the existing one first. |
E_FILE_UNREADABLE | Cannot read file: <path> | Verify the path exists and is readable; ensure --cwd points at the project root. |
E_FILE_MALFORMED | Malformed <format> in <path>: <detail> | Fix the syntax error and re-run; use bun run lint for a strict check. |
E_VALIDATION_FAILED | Validation failed for <target>: <detail> | Adjust the input to match the schema; see ralphy models show <target> for valid values. |
E_AGENT_UNSUPPORTED | Agent not supported in v1.0: <agent> | Use --agent claude|cursor|codex. |
E_WIZARD_NEEDS_TTY | Interactive wizard needs a TTY (<verb>) | Re-run on a terminal, or pass explicit flags to skip the wizard. |
E_TEMPLATE_VERSION_UNSUPPORTED | Template version not supported: <version> (template <id>) | Upgrade ralphy or downgrade the template’s version: field. |
E_TEMPLATE_INPUT_MISSING | Template <id> requires a <requirement> but none was supplied | Supply via the matching flag (e.g. --brand <slug>). |
E_TEMPLATE_SLUG_INVALID | Template slug rejected: <slug> (<reason>) | Pick an archetypal slug instead — describe what the template does. |
E_PROJECT_NOT_LINKED | No project linked or auto-detected from <cwd> | Pass --project <id> or cd into a workspace; see ralphy project list. |
Provider errors (exit 3)
| Code | Message template | Hint |
|---|---|---|
E_PROVIDER_HTTP | <provider> returned HTTP <status>: <detail> | Check the provider status page; retry or swap model. |
E_PROVIDER_AUTH | <provider> rejected the request as unauthorized | Re-check the API key with ralphy doctor. |
E_PROVIDER_RATE_LIMIT | <provider> rate-limited the request (retry after <retryAfter>s) | Wait and retry, or swap to a different model with --model. |
E_PROVIDER_INVALID_REQUEST | <provider> rejected the request as invalid: <detail> | Check params against ralphy models show <id>. |
E_PROVIDER_UNAVAILABLE | <provider> is unavailable: <detail> | Wait and retry; check the provider’s status page. |
E_BUDGET_EXCEEDED | Estimated cost <estimate> exceeds budget cap <cap> | Raise the cap via ralphy config set budgets.<scope> <usd> or trim the plan. |
Environment errors (exit 4)
| Code | Message template | Hint |
|---|---|---|
E_ENV_KEY_MISSING | Required API key not set: <key> | Run ralphy setup and paste the key, or export <key>=.... |
E_ENV_KEY_INVALID | API key for <provider> failed verification | Regenerate the key and re-run ralphy setup. |
E_DEP_MISSING | Required dependency not found on PATH: <dep> | Install with brew install <dep>, then re-run ralphy doctor. |
E_FS_PERMISSION | Cannot write to <path>: <detail> | Check directory permissions; ensure the user owns the workspace dir. |
Quality-gate refusals (exit 5)
| Code | Message template | Hint |
|---|---|---|
E_GATE_SCENARIO | Scenario quality gate refused: <detail> | Rework the scenario (rewrite hook, tighten VO, swap model) and retry. |
E_GATE_IMAGE | Image quality gate refused for <slot>: <detail> | Regenerate with a stronger reference or swap to a different image model. |
E_GATE_VIDEO | Video quality gate refused for <slot>: <detail> | Regenerate with different start/end frames or swap to a different video model. |
E_REF_REQUIRED | Reference required for named entity: <entity> | Attach a reference via ralphy ref add or pass --no-ref-consent to override. |
Runtime + cancellation
| Code | Class | Exit | Meaning |
|---|---|---|---|
E_INTERNAL | runtime | 1 | Catch-all internal error. File an issue. |
E_CANCELLED | cancelled | 130 | SIGINT. Append-only state preserved. |
Class semantics
User (2) — the user’s input is wrong. Re-running with the same flags will fail the same way. Fix the flag or the resource and retry. Provider (3) — the model provider misbehaved. Transient or model-specific. Retry, switch model, or wait. The hint names the swap. Env (4) — the local machine isn’t ready. Missing key, missing binary, fs permission.ralphy doctor catches all of these without making a model call.
Gate (5) — quality / policy refusal, not a technical failure. Two consecutive gate refusals on the same project is the agent’s stop signal — report concrete options, don’t render over the gate.
Runtime (1) — bug. The CLI surfaces this rather than silently exiting on uncaughtException / unhandledRejection. File an issue with the stderr payload.
Cancelled (130) — SIGINT. Append-only logs and manifests are preserved; re-running resumes from where you left off.
Append-only contract (post-v1.0)
The catalog is locked at v1.0 per01-D-07:
- Renames are forbidden.
- Removals are forbidden.
- Deprecating a code requires
deprecated: trueplusreplacedBy: "E_...". Deprecated codes continue to be emitted for at least one major version. - Adding a new code requires a CHANGELOG entry.
tests/unit/errors-catalog.test.ts enforce the contract:
- Every code matches
/^E_[A-Z][A-Z0-9_]+$/. - Catalog has fewer than 40 entries (v1.0 budget).
- Every entry carries a non-empty
message,hint,relatedDocs. - Hints never restate the message verbatim.
- Hints end with full-sentence punctuation.
- Deprecated entries name a known replacement.
- Placeholder braces are well-formed
{name}.
Placeholder template syntax
Messages and hints carry{name} tokens that interpolate at raise time from the context object:
Wiring into CI
Match on thecode field, not the message:
Related
- Output modes — the stderr shape in context
- Setup and doctor —
E_ENV_KEY_MISSING,E_DEP_MISSINGrecovery - cli/lib/errors/catalog.ts — source of truth
- tests/unit/errors-catalog.test.ts — the contract