unslothai/unsloth · error · CodexAuthError
Authorization callback was already used.
Error message
Authorization callback was already used.
What it means
Raised as CodexAuthError from _exchange_code when the OAuthFlow object already has consumed=True at entry. A flow is single-use: the first call to _exchange_code sets flow.consumed = True immediately, so any second attempt to exchange a code against the same flow (double-click, duplicate callback, retry after a partial failure) fails with this message before any network call is made.
Source
Thrown at studio/backend/core/inference/openai_codex_auth.py:296
raise CodexReauthorizationRequired(
"ChatGPT authorization is no longer valid. Please reconnect."
)
raise CodexAuthError("ChatGPT authorization failed. Please reconnect.")
try:
return response.json()
except Exception as exc:
raise CodexAuthError("ChatGPT returned an invalid authorization response.") from exc
async def _exchange_code(
flow: OAuthFlow,
code: str,
*,
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:View on GitHub (pinned to 203007d190)
Solutions
- Treat this error as benign in the success path — if the first exchange succeeded, the flow is connected; poll flow.status instead of re-completing.
- Fix the caller to submit the callback exactly once (disable the button after first click, dedupe on flow_id).
- Start a new flow if the first exchange genuinely failed; a consumed flow can never be reused.
- Check get_flow() status before calling complete_browser_flow and skip if status is already 'connected'.
Example fix
// before
@app.post("/complete")
async def complete(flow_id: str, callback_url: str):
return await codex_auth.complete_browser_flow(provider_id, flow_id, callback_url) # double-submit reuses flow
// after
@app.post("/complete")
async def complete(flow_id: str, callback_url: str):
flow = codex_auth.get_flow(provider_id, flow_id)
if flow.status == "connected":
return flow # idempotent: already exchanged
return await codex_auth.complete_browser_flow(provider_id, flow_id, callback_url) Defensive patterns
Strategy: validation
Validate before calling
flow = codex_auth.get_flow(provider_id, flow_id)
if flow.consumed:
if flow.status == "connected":
handle_already_connected(flow) # benign
else:
start_new_flow() # consumed and failed: cannot reuse
Type guard
def flow_is_reusable(flow: codex_auth.OAuthFlow) -> bool:
return not flow.consumed and flow.status == "pending" Try / catch
try:
await complete(flow_id, callback_url)
except codex_auth.CodexAuthError as exc:
if "already used" in str(exc):
flow = codex_auth.get_flow(provider_id, flow_id)
if flow.status == "connected":
return flow # idempotent success
raise Prevention
- Make completion requests idempotent client-side: submit once per flow_id.
- Poll flow.status instead of re-driving the exchange.
- Treat 'already used' + status 'connected' as success, not failure.
- Never cache and replay callback URLs against restarted flows.
When it happens
Trigger: The loopback HTTP handler receives two callback requests with valid codes; complete_browser_flow is invoked twice for the same flow_id; a client retries the completion request after a timeout while the first attempt already consumed the flow; the browser fires both an automatic redirect and a manual paste of the callback URL.
Common situations: Frontend double-submits the 'complete connection' form; user pastes the callback URL after the loopback server already handled the redirect; an HTTP client auto-retries a POST on connection reuse; a stale flow object was rehydrated from persistence while an in-flight exchange had already run.
Related errors
- Authorization flow is no longer active.
- ChatGPT credential update is busy. Please retry.
- ChatGPT returned an invalid access token.
- The ChatGPT account identifier was missing.
- ChatGPT returned an invalid token response.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/fd06d43e724cba5e.
Report an issue: GitHub.