{"record":{"id":"88be39c45db22ce3","repo":"coleam00/Archon","slug":"missing-authorization-code","errorCode":null,"errorMessage":"Missing authorization code.","messagePattern":"Missing authorization code\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/credentials/oauth-bridge.ts","lineNumber":222,"sourceCode":" * URL (PKCE), surface it on the session, wait for the pasted redirect URL /\n * code via the same `codeDeferred` the Pi manual flows use (so poll(code) and\n * abort semantics are identical), then exchange it directly — capturing the\n * `id_token` Pi drops. Runs NO local callback server (the #1963 wedge\n * pattern); the user pastes the final redirect URL or code back instead.\n */\nasync function runOpenAiManualLogin(session: OAuthSession): Promise<OpenAiOAuthCredentials> {\n  const flow = createOpenAiAuthorizeFlow();\n  session.url = flow.url;\n  if (session.mode === 'pending') session.mode = 'manual';\n  session.firstSignal.resolve(true);\n  // Rejected by abortSession on cancel/supersede/expiry — same as Pi flows.\n  const input = await session.codeDeferred.promise;\n  const parsed = parseOpenAiAuthorizationInput(input);\n  if (parsed.state && parsed.state !== flow.state) {\n    throw new Error('OAuth state mismatch.');\n  }\n  if (!parsed.code) {\n    throw new Error('Missing authorization code.');\n  }\n  // Returns its true type — the loginPromise join is typed as delivery.ts's\n  // loose `OAuthCredentials`, which this satisfies structurally (no cast).\n  return exchangeOpenAiAuthorizationCode(parsed.code, flow.verifier, session.abort.signal);\n}\n\n/**\n * Begin a subscription login for a vendor (anthropic/openai/github-copilot;\n * legacy claude/codex/copilot ids accepted). Kicks off the held login —\n * Pi's `login()` for anthropic/github-copilot, the Archon-owned PKCE flow for\n * openai — and returns once the first signal has populated the URL (manual)\n * or user-code (device), or a short timeout elapses.\n */\nexport async function startOAuth(userId: string, providerId: string): Promise<StartOAuthResult> {\n  // Expired sessions may also hold a callback server — include them in the\n  // settle-wait below so the port is free before the new login binds it.\n  const supersededSettled: Promise<void>[] = sweepExpired();\n  const provider = normalizeCredentialVendor(providerId);","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/credentials/oauth-bridge.ts#L204-L240","documentation":"Thrown by runOpenAiManualLogin when the pasted OpenAI authorization input parses successfully but contains no authorization `code` parameter. Without the code the PKCE token exchange cannot proceed, so the manual login fails explicitly rather than exchanging nothing.","triggerScenarios":"Awaiting session.codeDeferred and receiving input where parseOpenAiAuthorizationInput finds no code: pasting the bare authorization page URL without query params, pasting only the state parameter, pasting an error redirect (?error=access_denied), or pasting unrelated text/an empty line.","commonSituations":"User copies the callback URL before completing consent (no code yet); authorization was denied so the redirect carries an error instead of a code; pasting the wrong URL entirely; truncating the URL and losing the code query parameter.","solutions":["Complete the OAuth consent in the browser and copy the full final redirect URL including ?code=...","If the browser showed an authorization error (e.g. access denied), restart the login and approve the request.","Paste the entire URL — don't trim off query parameters.","Retry the login if the flow expired; a fresh attempt issues a fresh verifier and code."],"exampleFix":"// before (truncated, no code)\nhttps://auth.openai.com/oauth/callback?state=abc\n// after (complete redirect)\nhttps://auth.openai.com/oauth/callback?state=abc&code=xyz","handlingStrategy":"validation","validationCode":"import { parseOpenAiAuthorizationInput } from './openai-oauth';\nfunction hasAuthCode(input: string): boolean {\n  return !!parseOpenAiAuthorizationInput(input).code;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const creds = await loginPromise;\n} catch (e) {\n  if ((e as Error).message === 'Missing authorization code.') {\n    // retry the login; paste the complete final redirect URL with ?code=\n  } else throw e;\n}","preventionTips":["Copy the full final URL after consent completes — it must contain a code query parameter.","If the browser showed a denial/error redirect, restart the login rather than pasting that URL.","Don't truncate the callback URL when copying it into the prompt.","Retry promptly: codes and flows expire; a fresh login mints a fresh code."],"tags":["oauth","openai","missing-parameter","pkce"],"backgroundTag":"missing-oauth-code","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}