usestrix/strix · error · CodexAuthError
token_http_error
token_http_error
Error message
token_http_error: HTTP {status_code}: {detail} What it means
CodexAuthError with code `token_http_error` is raised when OpenAI's OAuth token endpoint returns an HTTP 4xx/5xx. The message embeds the status code and the first 300 bytes of the response body, so the exact failure (invalid_grant, invalid_client, server error) is visible in the exception text.
Source
Thrown at strix/config/codex.py:234
def _post_form(payload: dict[str, str]) -> dict[str, Any]:
detail = ""
try:
with requests.post(
TOKEN_URL,
data=payload,
headers={"Accept": "application/json"},
timeout=_TOKEN_TIMEOUT,
) as response:
status_code = response.status_code
body = response.content
if status_code >= 400:
detail = response.text[:300]
except requests.RequestException as exc:
raise CodexAuthError("unavailable", str(exc)) from exc
if status_code >= 400:
raise CodexAuthError("token_http_error", f"HTTP {status_code}: {detail}")
data = json.loads(body or b"{}")
if not isinstance(data, dict):
raise CodexAuthError("bad_response", "token endpoint returned non-object")
return data
def _record_from_token_response(
data: dict[str, Any], refresh_fallback: str | None = None
) -> dict[str, Any]:
access = data.get("access_token")
# A refresh response may omit refresh_token when it isn't rotated; keep the old one.
refresh = data.get("refresh_token") or refresh_fallback
expires_in = data.get("expires_in")
if not isinstance(access, str) or not access:
raise CodexAuthError("bad_response", "token response missing access_token")
if not isinstance(refresh, str) or not refresh:
raise CodexAuthError("bad_response", "token response missing refresh_token")
account_id = _account_id_from_jwt(access) or _account_id_from_jwt(View on GitHub (pinned to 8551339130)
Solutions
- Re-authenticate from scratch: `strix auth logout && strix auth login` to mint a fresh token pair
- If running multiple Strix processes concurrently, let the built-in cross-process refresh guard serialize them (it already exists); avoid deleting/copying ~/.strix/subscription-auth.json between machines
- Inspect the embedded `detail` body — `invalid_grant` means the refresh token is spent/expired (re-login), `invalid_client`/5xx means wait or check OpenAI status
- Retry once after a short delay for transient 5xx responses
Defensive patterns
Strategy: try-catch
Try / catch
from strix.config.codex import CodexAuthError
try:
token, account = get_valid_token()
except CodexAuthError as e:
if e.code == "token_http_error" and "invalid_grant" in str(e):
subprocess.run(["strix", "auth", "login"], check=True) # refresh token spent/expired
else:
raise Prevention
- Never copy ~/.strix/subscription-auth.json between machines or run parallel logins — refresh tokens are single-use
- Log out before switching accounts: `strix auth logout` invalidates cleanly instead of leaving stale refresh tokens
- Treat any 4xx containing invalid_grant as 're-login required'; only 5xx details are worth retrying
When it happens
Trigger: Calling `exchange_code()` with an expired/reused authorization code, or `refresh_tokens()` with a revoked, rotated, or already-spent refresh token (single-use refresh tokens); the token endpoint responding 400/401/403/5xx.
Common situations: Two Strix processes racing to refresh with the same single-use refresh token (the loser gets this error; the code has a peer-recovery path in get_valid_token); logging out and back in elsewhere which invalidates old tokens; clock-skewed or expired auth records in ~/.strix/subscription-auth.json; OpenAI-side 5xx incidents.
Related errors
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/2e588e38eed49a2b.
Report an issue: GitHub.