Error codes
Every non-zero exit writes a JSON error envelope to stderr:
{ "code": "rate_limit", "message": "Too many concurrent executions for this workspace", "hint": "Rate-limited. Back off and retry; see Retry-After header if surfaced.", "trace_id": "abc-123-def-456"}code is the stable, scriptable name. message is the server’s
human-readable description (may include identifiers, field paths). hint
is the CLI’s actionable suggestion. trace_id correlates with
server-side logs.
Server-originated codes
These come from the API’s error envelope and pass through the CLI:
| Code | Origin | Hint |
|---|---|---|
unauthenticated / unauthorized | 401 | Run athena login to re-authenticate. |
forbidden | 403 | Authenticated but lacking permission. Check workspace membership and role. |
not_found | 404 | Check the slug or ID. Use athena agent list or athena whoami to discover valid identifiers. |
conflict | 409 | Another writer changed this resource. Re-read with get <slug> and retry. |
rate_limit | 429 | Rate-limited. Back off and retry; see Retry-After header if surfaced. |
payment_required | 402 | Workspace credits exhausted. Top up before retrying. |
validation_error / bad_request | 400 | Request body or query failed validation — see message for the offending field. |
service_unavailable / external_service | 503 / 500 + code | Athena API is temporarily unavailable. Retry shortly. |
CLI-originated codes
These the CLI generates locally without contacting the server:
| Code | When | Hint |
|---|---|---|
missing_input | Required positional or flag not provided | (depends on flag, e.g. “Pass it as a positional argument: athena agent get <slug>.”) |
missing_api_url | No api_url resolved from flag / env / profile | Set api_url in [profile.<name>] or pass --api-url. |
unauthenticated | No token and no credentials | Run athena login or set ATHENA_API_TOKEN. (Hint adapts to your config source.) |
invalid_header_value | Env-derived header contains CRLF / non-ASCII bytes | Check the source (ATHENA_WORKSPACE_ID, --workspace, --idempotency-key) for stray newlines or invalid bytes. |
invalid_workflow | Pre-flight workflow validation failed | Fix each item listed in the message body and re-run. |
invalid_input | Body / query parameter parse failed locally | Check JSON syntax of the input. |
invalid_response | Server returned non-JSON body where JSON expected | Likely a server bug — file an issue with the trace_id. |
stream_error | SSE chunk decode failed OR 64 KiB buffer exceeded | The server may be sending malformed events. Retry; if it persists, file an issue. |
stream_lost | SSE closed cleanly but no terminal event | Look up the execution manually or retry with the same --idempotency-key. |
network | DNS / TLS / transport failure | Retry; check connectivity. |
refresh_failed | Token refresh round-trip failed | Run athena login to re-authenticate. |
config | Generic config-load failure | Check ~/.athena/config.toml syntax. |
no_config_base | No $HOME / %APPDATA% and no ATHENA_CONFIG_DIR | Set $HOME (Unix), %APPDATA% (Windows), or ATHENA_CONFIG_DIR. |
Pattern: branch on code not exit code
Exit codes are stable but coarse. For specific recovery paths, parse the
code field on stderr:
out=$(athena agent run my-agent --input @./i.json 2>err.json) || { code=$(jq -r '.code' err.json) case "$code" in rate_limit) sleep 30 && exec "$0" ;; payment_required) echo "top up credits"; exit 1 ;; invalid_workflow) echo "fix workflow:"; jq -r '.message' err.json; exit 2 ;; *) echo "unhandled $code"; exit 1 ;; esac}