# Exit Codes Every `omni-cli` invocation ends in exactly one of these codes. **Branch on the exit code, not on stdout's shape** — stdout carries the bare platform payload on success and nothing on failure (the structured error goes to stderr; see [Invocation & Envelope](https://omnicli.io/docs/invocation-and-envelope.md)). The same table ships in the CLI: `omni-cli errors`. | Code | Name | Meaning | |---|---|---| | 0 | `ok` | success | | 2 | `usage_error` | bad usage (incl. unknown/out-of-build command, --context both on an action) | | 3 | `context_unresolved` | no work/personal context resolved | | 4 | `not_authenticated` | no credential for the platform:context | | 5 | `auth_invalid` | credential rejected by the platform (401) | | 6 | `interactive_required` | an interactive terminal is required | | 7 | `permission_denied` | permission denied (403 / insecure local file perms) | | 8 | `not_found` | resource not found (404) | | 9 | `upstream_error` | upstream platform error (5xx / network) — outcome indeterminate; the CLI does not retry, the agent re-issues only if upstream.idempotent | | 10 | `entitlement_required` | entitlement required (paid stop / window expired) | | 11 | `update_required` | binary below min_binary_version — run omni-cli self-update | | 12 | `rate_limited` | platform rate-limited the request (429) — rejected before applying; the CLI does not retry, the agent may re-issue after upstream.retry_after_seconds | ## What to do, per code - `0` **ok** — read the payload from stdout. - `2` **usage_error** — fix the command: re-read the action's discovery entry; don't re-issue as-is. - `3` **context_unresolved** — pass `--context work` or `--context personal` explicitly. - `4` **not_authenticated** — a human must run the platform's login command on this machine. - `5` **auth_invalid** — a human must re-run the platform login; the stored credential was rejected. - `6` **interactive_required** — a human must run this command; agents never can. - `7` **permission_denied** — a human widens the credential's scopes (or fixes local file permissions). - `8` **not_found** — check the identifiers passed; the resource doesn't exist upstream. - `9` **upstream_error** — re-issue ONLY if the error's `upstream.idempotent` is true; the outcome is indeterminate. - `10` **entitlement_required** — a human reviews the plan or subscription; the action is outside the current entitlement. - `11` **update_required** — a human runs `omni-cli self-update`. - `12` **rate_limited** — wait `upstream.retry_after_seconds`, then re-issue; the request was rejected before applying. The CLI executes every action **exactly once and never retries** — not even reads. Retry decisions belong to the caller, informed by the error's `upstream` object (`idempotent`, `mutating`, `retry_after_seconds`).