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

  1. Re-authenticate from scratch: `strix auth logout && strix auth login` to mint a fresh token pair
  2. 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
  3. 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
  4. 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

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.