{"record":{"id":"a239c9d55b1c8b18","repo":"unslothai/unsloth","slug":"chatgpt-connection-requires-authorization","errorCode":null,"errorMessage":"ChatGPT connection requires authorization.","messagePattern":"ChatGPT connection requires authorization\\.","errorType":"exception","errorClass":"CodexAuthError","httpStatus":401,"severity":"error","filePath":"studio/backend/core/inference/openai_codex_auth.py","lineNumber":814,"sourceCode":"        if not record or record.get(\"marker\") != marker:\n            return\n    credential_secrets.delete_secret(\n        credential_secrets.OPENAI_CODEX_OAUTH_FLOW_KIND,\n        provider_id,\n    )\n\n\nasync def resolve_access(\n    provider_id: str,\n    *,\n    force_refresh: bool = False,\n    expected_access_token: str | None = None,\n) -> tuple[str, str]:\n    lock = _refresh_locks.setdefault(provider_id, asyncio.Lock())\n    async with lock:\n        bundle = load_oauth_bundle(provider_id)\n        if not bundle:\n            raise CodexAuthError(\"ChatGPT connection requires authorization.\")\n\n        if bundle.get(\"reauthorization_required\"):\n            raise CodexAuthError(\"ChatGPT authorization is no longer valid. Please reconnect.\")\n        if not force_refresh and bundle[\"expires_at\"] > time.time() + _REFRESH_SKEW_SECONDS:\n            return bundle[\"access_token\"], bundle[\"account_id\"]\n\n        # Multiple Studio workers may share the installation DB. Serialize with\n        # disconnect/delete and re-read so a completed refresh is reused.\n        async with provider_oauth_write_guard(provider_id):\n            bundle = load_oauth_bundle(provider_id)\n            if not bundle:\n                raise CodexAuthError(\"ChatGPT connection requires authorization.\")\n\n            if expected_access_token is not None and not secrets.compare_digest(\n                bundle[\"access_token\"], expected_access_token\n            ):\n                return bundle[\"access_token\"], bundle[\"account_id\"]\n            if not force_refresh and bundle[\"expires_at\"] > time.time() + _REFRESH_SKEW_SECONDS:","sourceCodeStart":796,"sourceCodeEnd":832,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/openai_codex_auth.py#L796-L832","documentation":"Raised as CodexAuthError by resolve_access when load_oauth_bundle(provider_id) returns no persisted credentials. resolve_access is the gate every inference call passes through to obtain an access token, so hitting it means the ChatGPT/Codex provider was never connected (or was disconnected) on this installation.","triggerScenarios":"Calling resolve_access for a provider whose OAuth bundle was never saved; after the user disconnected ChatGPT in Studio (bundle deleted); after a DB reset or migration dropped the oauth rows; wrong provider_id spelling; the same error is raised again inside the write-guard block if the bundle disappears between checks.","commonSituations":"Running inference before completing the ChatGPT connection; fresh installs; the disconnect path raced an in-flight request; multi-worker setups pointing at different installation DBs.","solutions":["Complete the ChatGPT connection flow (browser or device) for this provider, then retry.","Verify the provider_id passed to resolve_access matches the one used at connect time.","Confirm all Studio workers share one installation DB so credentials are visible everywhere.","In the UI, gate inference actions behind connection status (get_oauth_status) instead of letting unconfigured calls through."],"exampleFix":"// before\ntry:\n    token, account = await resolve_access(provider_id)\nexcept CodexAuthError:\n    raise  # surfaced as a crash to the user\n\n// after\ntry:\n    token, account = await resolve_access(provider_id)\nexcept CodexAuthError as exc:\n    if \"requires authorization\" in str(exc):\n        return RedirectResponse(\"/connect/chatgpt\")  # guide user to connect first\n    raise","handlingStrategy":"validation","validationCode":"if codex_auth.load_oauth_bundle(provider_id) is None:\n    show_connect_prompt(provider_id)  # avoid calling resolve_access entirely\n# or via status helper:\nif codex_auth.get_oauth_status(provider_id) != \"connected\":\n    show_connect_prompt(provider_id)","typeGuard":"def provider_is_connected(provider_id: str) -> bool:\n    return codex_auth.load_oauth_bundle(provider_id) is not None","tryCatchPattern":"try:\n    token, account = await codex_auth.resolve_access(provider_id)\nexcept codex_auth.CodexAuthError as exc:\n    if \"requires authorization\" in str(exc):\n        return RedirectResponse(\"/connect/chatgpt\")\n    raise","preventionTips":["Gate inference UI on connection status (get_oauth_status) before first call.","Verify provider_id spelling matches the connect-time id.","Point all workers at one installation DB.","Treat 'requires authorization' as a connect prompt, never a retry loop."],"tags":["oauth","authentication","not-connected","codex"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}