unslothai/unsloth · error · CodexAuthError

ChatGPT returned an invalid access token.

Error message

ChatGPT returned an invalid access token.

What it means

CodexAuthError from extract_chatgpt_account_id when the access token cannot be treated as a JWT: it must split into at least 2 dot-separated parts with a payload segment no larger than 16,384 chars (a deliberate bound before base64-decoding an untrusted string). Failing the split/size check, or the base64/JSON decode inside the try, both raise 'invalid access token' — the token is malformed, truncated, or not a JWT.

Source

Thrown at studio/backend/core/inference/openai_codex_auth.py:193

        bundle["reauthorization_required"] = True
        save_oauth_bundle(provider_id, bundle)


def _b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")


def create_pkce() -> tuple[str, str]:
    verifier = _b64url(secrets.token_bytes(48))
    challenge = _b64url(hashlib.sha256(verifier.encode("ascii")).digest())
    return verifier, challenge


def extract_chatgpt_account_id(access_token: str) -> str:
    """Decode only the bounded JWT payload needed as an upstream routing hint."""
    parts = access_token.split(".")
    if len(parts) < 2 or len(parts[1]) > 16_384:
        raise CodexAuthError("ChatGPT returned an invalid access token.")
    try:
        raw = base64.urlsafe_b64decode(parts[1] + "=" * (-len(parts[1]) % 4))
        payload = json.loads(raw)
    except Exception as exc:
        raise CodexAuthError("ChatGPT returned an invalid access token.") from exc
    account_id = payload.get("https://api.openai.com/auth", {}).get("chatgpt_account_id")
    if not isinstance(account_id, str) or not account_id or len(account_id) > 512:
        account_id = payload.get("https://api.openai.com/auth.chatgpt_account_id")
    if not isinstance(account_id, str) or not account_id or len(account_id) > 512:
        raise CodexAuthError("The ChatGPT account identifier was missing.")
    return account_id


def _validate_token_payload(body: Any, previous_refresh_token: str = "") -> dict[str, Any]:
    if not isinstance(body, dict):
        raise CodexAuthError("ChatGPT returned an invalid token response.")
    access_token = body.get("access_token")
    refresh_token = body.get("refresh_token") or previous_refresh_token

View on GitHub (pinned to 203007d190)

Solutions

  1. Inspect the token string: it should look like 'xxxxx.yyyyy.zzzzz' with a decodable middle segment — if not, re-run the OAuth flow to obtain a fresh token.
  2. Check that nothing in your storage/transport layer truncates or re-encodes the token (column length limits, URL encoding).
  3. If upstream genuinely changed the token format, update extract_chatgpt_account_id to match the new claim location/format.
Defensive patterns

Strategy: validation

Validate before calling

parts = access_token.split('.')
if len(parts) < 2 or len(parts[1]) > 16_384:
    raise ValueError('not a plausible JWT; re-authenticate')

Type guard

def looks_like_jwt(token: str) -> bool:
    parts = token.split('.')
    return len(parts) >= 2 and 0 < len(parts[1]) <= 16_384

Try / catch

try:
    account_id = extract_chatgpt_account_id(access_token)
except CodexAuthError as e:
    if 'invalid access token' in str(e):
        trigger_full_reauth(provider_id)  # token unusable; get a new one
    else:
        raise

Prevention

When it happens

Trigger: Passing an access token with fewer than 2 dot-separated segments (e.g. an opaque token or truncated string), a payload segment over 16KB, or a payload that is not valid base64url/JSON. Raised during token validation after any successful token exchange.

Common situations: Upstream auth changes where access_token stops being a JWT; string truncation when copying/storing the token; a proxy or logging layer mangling the header; non-ChatGPT OpenAI tokens with a different shape.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/21f49d757ca1670b. Report an issue: GitHub.