Skip to content

Schema: ExitCode

File: exit-code.json

Used by: REQ-F-001 · REQ-C-001 · REQ-C-013 · REQ-O-041


Purpose

Most CLI tools use only 0 (success) and 1 (failure), forcing agents to parse error messages to understand what went wrong and whether retrying is safe. ExitCode replaces this with a fixed table where every code carries two machine-readable guarantees: whether the operation is retryable and how far side effects progressed. An agent can decide its next action from the exit code alone.

Codes are sequential and grouped by category. The group boundaries are visible in this table — they are not encoded in the number itself.


Values

Success

Code Constant Retryable Side effects Agent action
0 SUCCESS complete Done

Execution — operation ran, something went wrong internally

Code Constant Retryable Side effects Agent action
1 GENERAL_ERROR depends unknown Inspect error.detail — last resort, use a specific code whenever one exists
2 PARTIAL_FAILURE no partial Inspect state before retrying — some writes occurred

Input — caller's fault; safe to retry after fixing

Code Constant Retryable Side effects Agent action
3 ARG_ERROR yes none Fix the input, retry immediately — zero side effects guaranteed
4 PRECONDITION depends none Resolve the precondition, then retry

Resource — about the addressed entity

Code Constant Retryable Side effects Agent action
5 NOT_FOUND no none Stop, or create the resource first
6 CONFLICT no none Resolve the conflict (delete, rename, or bump version)

Auth — identity and access

Code Constant Retryable Side effects Agent action
7 PERMISSION_DENIED no none Stop — valid credentials, wrong permissions; escalate or change approach
8 AUTH_REQUIRED yes* none Read error.code: TOKEN_EXPIRED → auto-refresh and retry; TOKEN_MISSING / TOKEN_INVALID → acquire credentials
9 PAYMENT_REQUIRED yes* none Attempt x402 payment if agent has payment permission, then retry

Infrastructure — external systems

Code Constant Retryable Side effects Agent action
10 TIMEOUT yes partial Back off and retry — some writes may have occurred
11 RATE_LIMITED yes none Retry after error.retry_after seconds
12 UNAVAILABLE yes none Service temporarily down — apply exponential back-off, retry

Routing — API surface changed

Code Constant Retryable Side effects Agent action
13 REDIRECTED yes none Use error.redirect.command verbatim; if error.redirect.permanent is true, memorize — never call the old form again

* Retryable only after the prerequisite condition is resolved.

Reserved ranges: - 14–63 framework extensions - 64–78 POSIX sysexits compatibility (optional mapping) - 79–125 command-specific (declare per REQ-C-001) - 126–255 shell-reserved — MUST NOT use


Examples

Valid

0
3
11

Invalid — and why

1

Violation: GENERAL_ERROR is a last resort. If the condition matches any specific code (3–13), use that code instead.

14

Violation: code 14 is in the framework extensions range (14–63) — reserved for future use. Commands must not emit it.

130

Violation: shell-reserved (128 + SIGINT). Codes 126–255 MUST NOT be used by framework commands.


Common mistakes

  • Using GENERAL_ERROR (1) as the default. Every condition that maps to a specific code must use that code. GENERAL_ERROR should be rare

  • Emitting ARG_ERROR (3) after a side effect. Code 3 carries a hard guarantee of zero side effects. If any write occurred before the error, emit PARTIAL_FAILURE (2) instead. The framework phase boundary (validate → execute) makes this automatic — do not bypass it

  • Treating AUTH_REQUIRED (8) as non-retryable. It is retryable — but only after resolving credentials. An agent that treats it as terminal will unnecessarily give up on auto-refreshable token expiry

  • Confusing PERMISSION_DENIED (7) and AUTH_REQUIRED (8). PERMISSION_DENIED means the credentials are valid but insufficient — retrying with the same credentials will never succeed. AUTH_REQUIRED means the credentials themselves are the problem

  • Treating all TIMEOUT (10) exits as non-retryable. For idempotent commands or read-only operations that time out without side effects, declare retryable: true, side_effects: "none" in ExitCodeEntry. Only declare retryable: false when partial writes may have occurred — the ExitCodeEntry invariant requires it when side_effects is "partial"


Agent interpretation

Rules for agents consuming exit codes at runtime. Apply these when the response is ambiguous, contradictory, or outside the known table.

Unknown code received - Code in 14–63 — framework extension; treat as GENERAL_ERROR (1) behavior: inspect error.detail, do not assume retryability - Code in 64–78 — POSIX sysexit; look up the POSIX meaning; treat as non-retryable unless the meaning clearly indicates a transient condition - Code in 79–125 — command-specific; consult that command's exit_codes declaration from the manifest before acting - Code in 126–255 — shell signal or exec error; the command likely never ran; safe to retry after investigating the environment - Code outside 0–255 — treat as GENERAL_ERROR (1); log for investigation

Contradictory signals - ok: true but exit code is non-zero — trust the exit code; discard ok - ok: false but exit code is 0 — treat as success; ok field is derived and may be stale in proxied responses - Exit code says retryable but error.retryable: false — trust error.retryable; it is the more specific signal

Retry budget - Codes marked retryable do not imply infinite retries — apply a retry budget (e.g. 3 attempts) before escalating - RATE_LIMITED (11) without error.retry_after — default to 60 seconds before retrying - UNAVAILABLE (12) — use exponential back-off starting at 1s, cap at 5 minutes

Side effects under uncertainty - Code is GENERAL_ERROR (1) (side effects unknown) — treat as side_effects: "partial"; inspect state before retrying - Code is TIMEOUT (10) — partial side effects are possible; do not assume the operation is idempotent without checking


Coding agent notes

Reading exit-code.json vendor extensions - x-enum-varnames: array of constant names parallel to enum values — use to generate the enum type (e.g. ExitCode.SUCCESS = 0) - x-enum-descriptions: array of descriptions parallel to enum values — use as docstrings on each enum member - x-groups: map of group name → list of codes — use to generate helper predicates (e.g. ExitCode.is_auth(code)) - x-code-ranges: documentation of reserved ranges — validate against these ranges at registration

Type representation - Generate ExitCode as an enum type, not integer constants — the type system must reject bare literals at the call site, not just at runtime - The enum must be serializable to its integer value for JSON output and process exit; do not serialize the name string

Validation to generate - A registration-time check that rejects any ExitCode value outside the declared enum members - A runtime check in dev/test mode that intercepts process.exit / sys.exit and asserts the code matches the command's declared set

Tests to generate - For each code: a test that emits the code and asserts the correct integer reaches the process exit - A negative test: emitting a literal integer (e.g. 5) at a call site that only accepts ExitCode must fail at compile/type-check time, not at runtime - A test that ARG_ERROR (3) is only emitted before any mock side-effect function is called

Anti-patterns - Do not generate if code == 7 comparisons — always compare against ExitCode.PERMISSION_DENIED - Do not generate a fallback except: exit(1) — map every known exception to a specific ExitCode - Do not expose a method that accepts int where ExitCode is expected


Implementation notes

  • Represent as a named constant / enum — never a bare integer at call sites. The framework must reject literal integers at command registration
  • retryable and side_effects values in this table describe the default agent behavior when no per-command ExitCodeEntry is available. When a command's manifest or --schema output includes an ExitCodeEntry for the received code, that entry takes precedence — the command may declare a code as non-retryable even if this table marks it retryable (e.g. TIMEOUT with partial side effects)
  • The hard invariants that commands may not relax: ARG_ERROR (3) is always side_effects: none; PARTIAL_FAILURE (2) is always retryable: false; SUCCESS (0) is the only code with side_effects: complete
  • ARG_ERROR (3) requires a hard phase boundary between validation and execution. No side effect may begin before this code can be emitted
  • AUTH_REQUIRED (8) intentionally does not distinguish expired from invalid at the exit code level. The distinction is in error.code in the JSON payload — a more controlled channel. See response-envelope.json ErrorDetail.code values: TOKEN_EXPIRED, TOKEN_INVALID, TOKEN_MISSING
  • REDIRECTED (13) requires the error.redirect field in the response. See response-envelope.json Redirect definition