unslothai/unsloth · error · CodexAuthError

ChatGPT returned an invalid token lifetime.

Error message

ChatGPT returned an invalid token lifetime.

What it means

Validation in _validate_token_payload for the token lifetime: expires_in (default 3600) must be int()-coercible — a TypeError/ValueError from int(expires_in) (e.g. a string like 'one_hour', None, or a nested object) is converted into CodexAuthError('ChatGPT returned an invalid token lifetime.'). Valid values are then clamped to [60, 30 days] before computing expires_at.

Source

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

    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
    expires_in = body.get("expires_in", 3600)
    if not isinstance(access_token, str) or not access_token:
        raise CodexAuthError("ChatGPT returned an invalid token response.")
    if not isinstance(refresh_token, str) or not refresh_token:
        raise CodexAuthError("ChatGPT did not return a refresh token.")
    try:
        expires_in = max(60, min(int(expires_in), 30 * 24 * 3600))
    except (TypeError, ValueError) as exc:
        raise CodexAuthError("ChatGPT returned an invalid token lifetime.") from exc
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "expires_at": int(time.time()) + expires_in,
        "account_id": extract_chatgpt_account_id(access_token),
    }


def save_oauth_bundle(provider_id: str, bundle: dict[str, Any]) -> None:
    credential_secrets.upsert_secret(
        credential_secrets.OPENAI_CODEX_OAUTH_KIND,
        provider_id,
        json.dumps(bundle, separators = (",", ":")),
    )


def load_oauth_bundle(provider_id: str) -> dict[str, Any] | None:
    raw = credential_secrets.get_secret(credential_secrets.OPENAI_CODEX_OAUTH_KIND, provider_id)

View on GitHub (pinned to 203007d190)

Solutions

  1. Retry the token request once — intermittent malformed bodies from intermediaries happen.
  2. If reproducible, log the raw expires_in value's type to confirm upstream drift, then update the coercion (e.g. accept dict['seconds'] or numeric strings) in _validate_token_payload.
  3. Ensure mocks/tests send expires_in as an integer.
Defensive patterns

Strategy: validation

Validate before calling

try:
    int(body.get('expires_in', 3600))
except (TypeError, ValueError):
    raise ValueError('expires_in must be integer-coercible')

Try / catch

try:
    bundle = _validate_token_payload(body)
except CodexAuthError as e:
    if 'token lifetime' in str(e):
        body = {**body, 'expires_in': 3600}  # sane default; log upstream drift
        bundle = _validate_token_payload(body)
    else:
        raise

Prevention

When it happens

Trigger: Token endpoint returning expires_in as a non-numeric string, null-extended JSON where it arrives as None (caught by int(None) -> TypeError), or an object like {'seconds': 3600} (int() raises TypeError).

Common situations: Upstream schema change stringifying durations; intermediaries rewriting numeric fields; mocked responses using wrong types.

Understand the failure class

Related errors


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