{"record":{"id":"d43b81b6015cb7e8","repo":"unslothai/unsloth","slug":"paste-the-complete-localhost-chatgpt-callback-url","errorCode":null,"errorMessage":"Paste the complete localhost ChatGPT callback URL.","messagePattern":"Paste the complete localhost ChatGPT callback URL\\.","errorType":"exception","errorClass":"CodexAuthError","httpStatus":400,"severity":"warning","filePath":"studio/backend/core/inference/openai_codex_auth.py","lineNumber":619,"sourceCode":"        if flow.server:\n            flow.server.close()\n    return flow\n\n\nasync def complete_browser_flow(provider_id: str, flow_id: str, callback_url: str) -> OAuthFlow:\n    flow = get_flow(provider_id, flow_id)\n    if flow.method != \"browser\" or flow.status != \"pending\" or flow.consumed:\n        raise CodexAuthError(\"Authorization flow is no longer active.\")\n    parsed = urlparse(callback_url)\n    expected = urlparse(flow.redirect_uri)\n    if (\n        parsed.scheme != expected.scheme\n        or parsed.hostname != expected.hostname\n        or parsed.port != expected.port\n        or parsed.path != expected.path\n        or parsed.fragment\n    ):\n        raise CodexAuthError(\"Paste the complete localhost ChatGPT callback URL.\")\n    query = parse_qs(parsed.query)\n    if not secrets.compare_digest(query.get(\"state\", [\"\"])[0], flow.state):\n        raise CodexAuthError(\"Authorization state did not match.\")\n    code = query.get(\"code\", [\"\"])[0]\n    if not code:\n        raise CodexAuthError(\"The callback URL did not contain an authorization code.\")\n    await _exchange_code(flow, code)\n    return flow\n\n\nasync def cancel_flow(flow_id: str) -> None:\n    flow = _flows.pop(flow_id, None)\n    if not flow:\n        return\n    flow.status = \"cancelled\"\n    if flow.task:\n        flow.task.cancel()\n    if flow.cleanup_task and flow.cleanup_task is not asyncio.current_task():","sourceCodeStart":601,"sourceCodeEnd":637,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/openai_codex_auth.py#L601-L637","documentation":"Raised as CodexAuthError by complete_browser_flow when the user-pasted callback URL does not structurally match the flow's registered redirect_uri: scheme, hostname, port, or path differ, or the URL contains a fragment. Because the expected URI is a localhost loopback with a dynamically assigned port, the port comparison is where most legitimate attempts fail.","triggerScenarios":"The user pastes the authorization URL instead of the redirect URL; copies the URL without the port (browsers hide default ports); the paste is truncated before the path; a fragment (#...) is appended by the browser or an extension; the redirect landed on a different port than flow.redirect_uri recorded.","commonSituations":"Manual paste workflows where users grab the wrong URL from the address bar; mobile browsers stripping ports; URL shorteners or copy apps mangling the URL; users typing the URL by hand.","solutions":["Copy the FULL localhost URL from the browser address bar after the ChatGPT redirect, including http://, 127.0.0.1, the port, the path, and the entire query string.","Do not use the auth.openai.com consent URL — only the 127.0.0.1 callback URL is valid here.","If the port changed (flow restarted), paste the callback into the NEW flow's completion, not the old one.","Trim whitespace and ensure no trailing fragment was added when pasting."],"exampleFix":"// before\nawait codex_auth.complete_browser_flow(provider_id, flow_id, \"https://auth.openai.com/authorize?...\")\n\n// after\n# the URL the browser landed on AFTER consent, e.g.:\nawait codex_auth.complete_browser_flow(\n    provider_id,\n    flow_id,\n    \"http://127.0.0.1:53742/callback?state=...&code=...\",\n)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\np = urlparse(callback_url)\ne = urlparse(flow.redirect_uri)\nif (p.scheme, p.hostname, p.port, p.path) != (e.scheme, e.hostname, e.port, e.path) or p.fragment:\n    reject(\"pasted URL is not this flow's exact localhost callback\")","typeGuard":"def callback_matches_redirect(callback_url: str, flow: codex_auth.OAuthFlow) -> bool:\n    p, e = urlparse(callback_url), urlparse(flow.redirect_uri)\n    return (\n        p.scheme == e.scheme\n        and p.hostname == e.hostname\n        and p.port == e.port\n        and p.path == e.path\n        and not p.fragment\n    )","tryCatchPattern":"try:\n    flow = await codex_auth.complete_browser_flow(provider_id, flow_id, pasted)\nexcept codex_auth.CodexAuthError as exc:\n    if \"complete localhost\" in str(exc):\n        ask_user_to_recopy_url()  # input problem; new flow not required\n    else:\n        raise","preventionTips":["Instruct users to copy the full post-redirect localhost URL including port and query.","Validate the URL shape client-side before submitting (same comparisons as the guard).","Warn when the pasted host is auth.openai.com — that is the consent page, not the callback.","Trim whitespace and strip fragments on paste."],"tags":["oauth","callback-url","user-input","loopback","codex"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}