{"record":{"id":"fad22a148704b0dc","repo":"coleam00/Archon","slug":"login-cancelled","errorCode":null,"errorMessage":"Login cancelled","messagePattern":"Login cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"packages/core/src/credentials/openai-oauth.ts","lineNumber":175,"sourceCode":"  operation: 'exchange' | 'refresh',\n  signal?: AbortSignal\n): Promise<OpenAiTokenResponse> {\n  let response: Response;\n  try {\n    response = await fetch(OPENAI_TOKEN_URL, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n      body,\n      // The 30s ceiling applies ALWAYS — combined with the caller's session\n      // signal when present. Without it, a hung token endpoint would leave a\n      // bridge login reporting `pending` for the session's full 10-minute TTL.\n      signal: signal\n        ? AbortSignal.any([signal, AbortSignal.timeout(30_000)])\n        : AbortSignal.timeout(30_000),\n    });\n  } catch (error) {\n    if (signal?.aborted) {\n      throw new Error('Login cancelled');\n    }\n    if (error instanceof Error && error.name === 'TimeoutError') {\n      throw new Error(`OpenAI token ${operation} request timed out.`);\n    }\n    throw new Error(\n      `OpenAI token ${operation} request failed: ${error instanceof Error ? error.message : String(error)}`\n    );\n  }\n  if (!response.ok) {\n    // Strip the error body down to the OAuth `error` code: this message flows\n    // into the bridge's session.detail (and on to the browser/CLI), and OpenAI\n    // error bodies can carry account identifiers. Never include the raw body.\n    const text = await response.text().catch(() => '');\n    let errorCode = '';\n    try {\n      const parsed = JSON.parse(text) as { error?: unknown };\n      if (typeof parsed.error === 'string') {\n        errorCode = parsed.error;","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/credentials/openai-oauth.ts#L157-L193","documentation":"postTokenRequest in packages/core/src/credentials/openai-oauth.ts:175 rethrows caller cancellation as 'Login cancelled'. When the fetch to OPENAI_TOKEN_URL fails and the caller-supplied AbortSignal (the bridge session's abort signal) is aborted, the error is normalized to this message instead of a misleading network error. It means the token exchange/refresh was cancelled, e.g. by cancelOAuth, supersession, or session expiry.","triggerScenarios":"Calling cancelOAuth, starting a newer login that supersedes the session, or the session TTL expiring while the OpenAI token exchange fetch is in flight; the aborted fetch rejects and postTokenRequest maps it to 'Login cancelled'.","commonSituations":"User clicking 'Cancel' during login; a second login attempt superseding the first; a login left idle until TTL expiry while the token exchange was still pending.","solutions":["Nothing to fix — restart the login by calling startOAuth again after any cancellation.","Avoid cancelling a login unless intended; be aware supersession also cancels in-flight token exchanges.","Handle this message/flow in the UI as a normal cancel outcome, not a bug report."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Do not proceed when the session's abort signal is already aborted\nif (session.abort.signal.aborted) {\n  throw new Error('Login already cancelled; start a new one.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await exchangeOpenAiAuthorizationCode(code, verifier, signal);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Login cancelled') {\n    // Expected outcome of cancelOAuth/supersession/expiry — restart login if desired\n    return;\n  }\n  throw e;\n}","preventionTips":["Treat this as the expected cancel path; log at info, not error.","Design UI so cancel actions intentionally terminate pending exchanges.","Remember supersession also cancels: don't fire a second login expecting the first to finish."],"tags":["oauth","abort","cancelled"],"backgroundTag":"request-aborted","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}