{"record":{"id":"a85c91bee2cc786e","repo":"unslothai/unsloth","slug":"chatgpt-returned-an-invalid-token-lifetime","errorCode":null,"errorMessage":"ChatGPT returned an invalid token lifetime.","messagePattern":"ChatGPT returned an invalid token lifetime\\.","errorType":"exception","errorClass":"CodexAuthError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/openai_codex_auth.py","lineNumber":220,"sourceCode":"    if not isinstance(account_id, str) or not account_id or len(account_id) > 512:\n        raise CodexAuthError(\"The ChatGPT account identifier was missing.\")\n    return account_id\n\n\ndef _validate_token_payload(body: Any, previous_refresh_token: str = \"\") -> dict[str, Any]:\n    if not isinstance(body, dict):\n        raise CodexAuthError(\"ChatGPT returned an invalid token response.\")\n    access_token = body.get(\"access_token\")\n    refresh_token = body.get(\"refresh_token\") or previous_refresh_token\n    expires_in = body.get(\"expires_in\", 3600)\n    if not isinstance(access_token, str) or not access_token:\n        raise CodexAuthError(\"ChatGPT returned an invalid token response.\")\n    if not isinstance(refresh_token, str) or not refresh_token:\n        raise CodexAuthError(\"ChatGPT did not return a refresh token.\")\n    try:\n        expires_in = max(60, min(int(expires_in), 30 * 24 * 3600))\n    except (TypeError, ValueError) as exc:\n        raise CodexAuthError(\"ChatGPT returned an invalid token lifetime.\") from exc\n    return {\n        \"access_token\": access_token,\n        \"refresh_token\": refresh_token,\n        \"expires_at\": int(time.time()) + expires_in,\n        \"account_id\": extract_chatgpt_account_id(access_token),\n    }\n\n\ndef save_oauth_bundle(provider_id: str, bundle: dict[str, Any]) -> None:\n    credential_secrets.upsert_secret(\n        credential_secrets.OPENAI_CODEX_OAUTH_KIND,\n        provider_id,\n        json.dumps(bundle, separators = (\",\", \":\")),\n    )\n\n\ndef load_oauth_bundle(provider_id: str) -> dict[str, Any] | None:\n    raw = credential_secrets.get_secret(credential_secrets.OPENAI_CODEX_OAUTH_KIND, provider_id)","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/openai_codex_auth.py#L202-L238","documentation":"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.","triggerScenarios":"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).","commonSituations":"Upstream schema change stringifying durations; intermediaries rewriting numeric fields; mocked responses using wrong types.","solutions":["Retry the token request once — intermittent malformed bodies from intermediaries happen.","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.","Ensure mocks/tests send expires_in as an integer."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"try:\n    int(body.get('expires_in', 3600))\nexcept (TypeError, ValueError):\n    raise ValueError('expires_in must be integer-coercible')","typeGuard":null,"tryCatchPattern":"try:\n    bundle = _validate_token_payload(body)\nexcept CodexAuthError as e:\n    if 'token lifetime' in str(e):\n        body = {**body, 'expires_in': 3600}  # sane default; log upstream drift\n        bundle = _validate_token_payload(body)\n    else:\n        raise","preventionTips":["Treat malformed expires_in as upstream drift: log it and consider a default lifetime rather than failing the whole auth.","Send integers for expires_in in all mocks/tests.","Monitor token-endpoint response shapes to catch schema changes early."],"tags":["oauth","response-validation","expires-in","codex"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}