unslothai/unsloth · error · CodexAuthError
Authorization flow was cancelled before credentials were sav
Error message
Authorization flow was cancelled before credentials were saved.
What it means
Raised as CodexAuthError inside _exchange_code after a successful token exchange when flow.persist_bundle is None or flow.status is no longer 'pending'. The tokens were obtained from OpenAI but there is no way to save them because the flow was cancelled (or never wired with a persistence callback) at that moment. The except block then marks the flow as errored and persists the terminal state before re-raising.
Source
Thrown at studio/backend/core/inference/openai_codex_auth.py:310
verifier: str | None = None,
redirect_uri: str | None = None,
) -> None:
if flow.consumed:
raise CodexAuthError("Authorization callback was already used.")
flow.consumed = True
try:
body = await _token_request(
{
"grant_type": "authorization_code",
"client_id": OPENAI_CODEX_CLIENT_ID,
"code": code,
"redirect_uri": redirect_uri or flow.redirect_uri,
"code_verifier": verifier or flow.verifier,
}
)
bundle = _validate_token_payload(body)
if flow.persist_bundle is None or flow.status != "pending":
raise CodexAuthError("Authorization flow was cancelled before credentials were saved.")
persisted = flow.persist_bundle(flow.provider_id, bundle)
if persisted is not None:
await persisted
except Exception:
flow.status = "error"
flow.message = "ChatGPT authorization failed. Please reconnect."
await _persist_terminal_flow(flow)
raise
flow.status = "connected"
if flow.server:
flow.server.close()
flow.server = None
async def _loopback_handler(
flow: OAuthFlow, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
) -> None:
try:View on GitHub (pinned to 203007d190)
Solutions
- Simply start a new connection flow — the obtained credentials are intentionally discarded, nothing was saved.
- If building custom flows, always pass persist_bundle when constructing the flow so a successful exchange can be saved.
- Avoid cancelling flows while the user's browser is still on the OpenAI consent page; wait for the flow TTL to expire instead.
- Check flow.status == 'pending' before issuing cancel to reduce the race window.
Example fix
// before
flow = OAuthFlow(id=..., provider_id=..., method="browser", ...) # no persist_bundle
// after
flow = OAuthFlow(
id=...,
provider_id=...,
method="browser",
persist_bundle=persist_oauth_bundle, # successful exchange can now be saved
...,
) Defensive patterns
Strategy: try-catch
Validate before calling
flow = codex_auth.get_flow(provider_id, flow_id)
assert flow.status == "pending", f"flow is {flow.status}; completing now risks a cancel race"
assert flow.persist_bundle is not None, "flow lacks persistence; exchange cannot be saved" Type guard
def flow_is_persistable(flow: codex_auth.OAuthFlow) -> bool:
return flow.persist_bundle is not None and flow.status == "pending" Try / catch
try:
flow = await complete(flow_id, callback_url)
except codex_auth.CodexAuthError as exc:
if "cancelled" in str(exc):
flow = await start_new_flow(provider_id) # tokens were discarded; restart
else:
raise Prevention
- Always construct flows with a persist_bundle callback.
- Do not cancel flows while the user's browser is still on the consent page.
- After cancelling, always mint a new flow rather than resuming.
- Test the cancel-then-callback race explicitly (see test_openai_codex_subscription.py 'cancelled' case).
When it happens
Trigger: User clicks 'Cancel' in Studio while the browser is completing the OAuth redirect — cancel_flow sets status='cancelled' and the race is lost when _exchange_code reaches the persist step; a flow object constructed without a persist_bundle callable; the flow expired and get_flow set status='error' just before the callback arrived.
Common situations: Race between cancellation and the authorization redirect landing; tests or custom integrations building OAuthFlow objects directly without the persistence callback; a long user delay on the ChatGPT consent page until after the flow was cancelled.
Related errors
- Authorization flow was not found or expired.
- ChatGPT connection was disconnected during refresh.
- ChatGPT credential update is busy. Please retry.
- ChatGPT returned an invalid access token.
- The ChatGPT account identifier was missing.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/dd3e916072b1acda.
Report an issue: GitHub.