unslothai/unsloth · error · CodexAuthError

Authorization flow is no longer active.

Error message

Authorization flow is no longer active.

What it means

Raised as CodexAuthError by complete_browser_flow as a precondition guard: the located flow must be a browser-method flow, still in status 'pending', and not yet consumed. Any deviation — cancelled flow, errored flow, expired flow, completed flow, or a device-method flow submitted to the browser completion API — raises immediately before the callback URL is parsed.

Source

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

    elif flow is None or flow.provider_id != provider_id:
        flow = _load_persisted_oauth_flow(provider_id, flow_id)
        if flow is None:
            raise CodexAuthError("Authorization flow was not found or expired.")
        _flows[flow.id] = flow
    if time.time() >= flow.expires_at and flow.status == "pending":
        flow.status = "error"
        flow.message = "Authorization expired. Start a new connection."
        if flow.task:
            flow.task.cancel()
        if flow.server:
            flow.server.close()
    return flow


async def complete_browser_flow(provider_id: str, flow_id: str, callback_url: str) -> OAuthFlow:
    flow = get_flow(provider_id, flow_id)
    if flow.method != "browser" or flow.status != "pending" or flow.consumed:
        raise CodexAuthError("Authorization flow is no longer active.")
    parsed = urlparse(callback_url)
    expected = urlparse(flow.redirect_uri)
    if (
        parsed.scheme != expected.scheme
        or parsed.hostname != expected.hostname
        or parsed.port != expected.port
        or parsed.path != expected.path
        or parsed.fragment
    ):
        raise CodexAuthError("Paste the complete localhost ChatGPT callback URL.")
    query = parse_qs(parsed.query)
    if not secrets.compare_digest(query.get("state", [""])[0], flow.state):
        raise CodexAuthError("Authorization state did not match.")
    code = query.get("code", [""])[0]
    if not code:
        raise CodexAuthError("The callback URL did not contain an authorization code.")
    await _exchange_code(flow, code)
    return flow

View on GitHub (pinned to 203007d190)

Solutions

  1. Poll get_flow().status first; only call complete_browser_flow while status == 'pending'.
  2. Make the completion call idempotent client-side (disable after first submit).
  3. If the flow was cancelled/expired, start a new flow — completion can never resume it.
  4. Route device flows to their own completion path, not the browser one.

Example fix

// before
await codex_auth.complete_browser_flow(provider_id, flow_id, callback_url)  # retried blindly

// after
flow = codex_auth.get_flow(provider_id, flow_id)
if flow.method == "browser" and flow.status == "pending" and not flow.consumed:
    flow = 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.method != "browser" or flow.status != "pending" or flow.consumed:
    start_new_flow(provider_id)  # completion is only legal on a pending, unconsumed browser flow

Type guard

def flow_accepts_completion(flow: codex_auth.OAuthFlow) -> bool:
    return flow.method == "browser" and flow.status == "pending" and not flow.consumed

Try / catch

try:
    flow = await codex_auth.complete_browser_flow(provider_id, flow_id, callback_url)
except codex_auth.CodexAuthError as exc:
    if "no longer active" in str(exc):
        flow = codex_auth.get_flow(provider_id, flow_id)
        if flow.status == "connected":
            return flow  # already done
        start_new_flow(provider_id)
    else:
        raise

Prevention

When it happens

Trigger: Calling complete_browser_flow twice (second call sees consumed=True or status 'connected'); submitting a device flow's flow_id to the browser endpoint; the flow was cancelled or expired (get_flow already flipped pending flows to 'error' on TTL); the exchange failed earlier and status became 'error'.

Common situations: Frontend retries a completion request after timeout; user clicks cancel then pastes the URL anyway; polling UX resubmits after the flow already finished; mixing up flow ids between device and browser connections.

Related errors


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