Error contract reference for user-facing CLI and exported helper behavior.
0: successful execution1: usage error, invalid arguments, sync/persistence failure, or command failure
- Human-readable command output is written to
stdout. - Argument/usage and failure diagnostics are written to
stderr. - On invalid command/arguments, usage text is printed with a non-zero exit code.
Examples:
- unknown subcommand:
Unknown command: <name>plus usage switchwith missing index:Missing index. Usage: codex-multi-auth switch <index>switchwith invalid index:Invalid index: <value>
The following commands support --json and produce pretty-printed JSON objects:
codex-multi-auth forecast --jsoncodex-multi-auth report --jsoncodex-multi-auth fix --jsoncodex-multi-auth doctor --jsoncodex-multi-auth verify-flagged --json
Compatibility guarantees:
- Output is valid JSON.
commandfield identifies the command family.- Documented top-level sections remain stable unless a migration note is provided.
- Upstream entitlement-like 404 payloads are normalized to
403withentitlement_errorpayloads. - Entitlement errors are not treated as rate limits.
- Upstream usage-limit indicators normalize to rate-limit semantics.
handleErrorResponsemay return parsedrateLimit.retryAfterMsmetadata.
- Error responses are normalized to JSON error payloads with a stable
error.messagefield. - Diagnostics may include request/correlation IDs when available.
The request layer's thrown errors are backed by the typed hierarchy in lib/errors.ts (base class CodexError, which extends Error and carries a stable code string):
refreshAndUpdateTokenthrowsCodexAuthError(code: "CODEX_AUTH_ERROR") with the messageFailed to refresh token, authentication requiredon any refresh failure. The error carries aretryableboolean (transient network/lock failures are retryable; invalid-grant style failures are not) and, where available,causeandcontext(refreshFailureReason,statusCode).- Catch sites may rely on
instanceof CodexAuthError(or the structuralcodeproperty) plusretryableto decide whether to re-attempt or force re-authentication. - HTTP error responses are returned as normalized
Responsepayloads (see above), not thrown, so they intentionally have noErrorclass.
The default-on localhost Responses proxy returns JSON error payloads with a stable error.code field.
| Code | HTTP status | Meaning |
|---|---|---|
runtime_rotation_proxy_not_found |
404 |
Request path or method is outside the supported Responses/model discovery surface |
runtime_rotation_proxy_unauthorized |
401 |
Local request did not include the per-process proxy client key |
runtime_rotation_proxy_payload_too_large |
413 |
Request body exceeded the proxy safety cap |
codex_runtime_rotation_pool_exhausted |
429 or 503 |
No managed account can currently service the runtime request |
codex_pinned_account_unavailable |
503 |
A manual pin is set (via codex-multi-auth switch) but the pinned account is rate-limited, cooling down, disabled, or blocked by policy. Run codex-multi-auth status for details, or codex-multi-auth unpin to allow rotation |
codex_runtime_rotation_proxy_error |
500 |
Proxy failed before forwarding the request |
Pool exhaustion includes a reason, retry_after_ms, and a hint to run codex-multi-auth rotation status. Pinned-account-unavailable responses include a pinnedAccountIndex field identifying the pinned account, a structured reason field carrying the runtime skip reason (for example rate-limited, cooling-down:auth-failure, circuit-open, disabled, workspace-disabled, policy-blocked, missing, already-attempted) or null when no reason was recorded, and an account_skip_reasons map keyed by account index that mirrors the pool-exhausted response shape. The human-readable message appends the same reason in parentheses when present (see issue #486).
For selected exported helper APIs, options-object forms were added without removing positional signatures.
Supported dual-call forms include:
selectHybridAccount(...)andselectHybridAccount({ ... })exponentialBackoff(...)andexponentialBackoff({ ... })getTopCandidates(...)andgetTopCandidates({ ... })createCodexHeaders(...)andcreateCodexHeaders({ ... })getRateLimitBackoffWithReason(...)andgetRateLimitBackoffWithReason({ ... })transformRequestBody(...)andtransformRequestBody({ ... })
Invalid named-parameter calls (missing or wrongly typed required fields, or unknown keys) throw a native TypeError with a <helper> requires ... message — for example, createCodexHeaders throws TypeError: createCodexHeaders requires accountId and accessToken. This is a deliberate, shared convention across the dual-call helpers and is not wrapped in a CodexError subclass.
lib/errors.ts exports a CodexError hierarchy (CodexApiError, CodexAuthError, CodexNetworkError, CodexValidationError, CodexRateLimitError, StorageError, CodexUnavailableError). Every subclass carries a stable string code (the ErrorCode constants) plus class-specific fields, so callers can branch on instanceof or code instead of message text.
Startup-validation guarantees backed by these types:
startRuntimeRotationProxythrowsCodexValidationErrorwithfield: "clientApiKey"when no client API key is supplied, andCodexValidationErrorwithfield: "host"(offending host incontext.host) when asked to bind a non-loopback host. Messages are unchanged from earlier releases; only the class tightened.savePluginConfigaborts withStorageError(code: "UNREADABLE",path= the config file, actionablehint, read-classifier message ascause) when the existing config file cannot be read. Messages are unchanged from earlier releases; only the class tightened.