Skip to main content
Every ish command exits with a semantic code. Branch on the code in CI and agent loops, and parse the JSON envelope (in --json mode) for the structured cause. The code answers “what class of failure”, the envelope answers “what exactly, and what to do next”.

Exit codes

In --json mode, errors carry a structured envelope: error, error_code, retryable, and often suggestions, error_kind, and an example invocation that fixes the call. The map is computed by exitCodeFromError in command-helpers.ts. A successful command exits 0; -h, --help and -V, --version also exit 0.

How a failure resolves to a code

The resolver checks the most specific signal first, so a tagged error always beats a status guess and a status guess always beats a message-regex sniff.
1

Billing walls (exit 1)

A usage_limit_reached or insufficient_credits error is checked before the status map. These arrive as HTTP 403 but are not auth failures, so they exit 1, not 3. An agent branching “exit 3 means re-login” would otherwise loop forever on a quota cap.
2

HTTP status (exit 3, 4, 2, 5)

For other API errors: 401 and 403 exit 3, 404 exits 4, 400 and 422 exit 2. A retryable status (408, 429, or any 5xx) exits 5.
3

Tagged error code (exit 2, 3, 4)

A CLI-thrown error carrying error_code is mapped directly: usage_error exits 2, auth_failed exits 3, not_found exits 4.
4

Retryability and error kind (exit 5, 2)

wait_timeout and any error pre-declaring retryable: true exit 5. A TunnelInactive or BotAuthError kind exits 5 (the cause is fixable, then retry); ConfirmationRequired or BotShapeError exit 2.
5

Network cause (exit 5)

DNS and connection failures (ENOTFOUND, ECONNREFUSED, ECONNRESET, ETIMEDOUT, EAI_AGAIN) are transient and exit 5.
6

Fallback (exit 1)

Anything unclassified exits 1.
Out of credits exits 1, not 3. A 402 or a billing 403 is a quota cap, not an auth problem. Re-running ish login will not fix it. Read error_code to tell insufficient_credits and usage_limit_reached apart from a real auth failure.

The error envelope

In --json mode (or when stdout is piped, which auto-selects JSON), a failing command writes a single-line JSON object to stderr and the exit code to the process. Human mode prints Error: <message> followed by indented suggestion lines instead. See global flags for when JSON is auto-selected.

Always present

string
The human-readable message. For API errors, server-internal entity names are remapped to the user-facing vocabulary before printing.
string
The stable machine code to branch on. Prefer this over the message text, which can change. See the error codes table.
boolean
Whether the same call may succeed if retried unchanged. true corresponds to exit 5.

Often present

These fields appear only when the failure carries them, so a consumer must treat each as optional.
number
The HTTP status, present only on errors that came back from the API.
string[]
Recovery hints, for example “pass --study or run ish study use”. Merged from the server response, the error instance, and the CLI’s own code-to-hint mapping.
array
Field-level validation detail from a 422 response. Each entry carries loc, msg, type, and, where the field is an enum, allowed_values.
string
A structured kind for failures that have one, such as TunnelInactive, ConfirmationRequired, BotAuthError, or BotShapeError.
string
A corrected invocation that fixes the call, for example the same command with --yes appended when a destructive action needs confirmation in --json mode.
string
A one-line hint on a client-side validation error.
string[]
The accepted values when a validation error rejected an enum-shaped input.
string[]
The set a filter argument could have matched (for example the frames a --frame value could resolve to).
object
How far a wait got before it timed out, on a wait_timeout error.
string[]
Participants seeded before a dispatch failed. Resume with these rather than re-seeding, which would create duplicates. A matching seeded_but_not_dispatched_aliases rides alongside.
string
A hint to report the failure, present only on genuine faults (not on usage errors).

Billing fields

On a usage_limit_reached error, the envelope also carries tier, limit, current, max, and upgrade_url. See run vs ask for what draws credits.

Error codes

The error_code field is the stable contract. API errors map from the HTTP status; CLI-thrown errors set the code directly.

Example

A study id that does not exist returns exit 4 with this envelope on stderr:
Branch on the code in a script:
Branch on the exit code for control flow and read error_code for the precise cause. Both are stable; the error message text is not.