{"record":{"id":"4119e6a278f0d36a","repo":"tinyhumansai/openhuman","slug":"openrouter-oauth-was-cancelled","errorCode":null,"errorMessage":"OpenRouter OAuth was cancelled.","messagePattern":"OpenRouter OAuth was cancelled\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"app/src/utils/openrouterOAuth.ts","lineNumber":131,"sourceCode":"  // sibling OAuthProviderButton flow, which trusts the bound port).\n  parsed.hostname = 'localhost';\n  return parsed.toString();\n}\n\nexport async function connectOpenRouterViaOAuth(deps: OpenRouterOAuthDeps = {}): Promise<string> {\n  const startLoopbackListener = deps.startLoopbackListener ?? startLoopbackOauthListener;\n  const openExternalUrl = deps.openExternalUrl ?? openUrl;\n  const fetchImpl = deps.fetchImpl ?? fetch;\n  const signal = deps.signal;\n\n  const loopback = await startLoopbackListener({ port: OPENROUTER_LOOPBACK_PORT });\n  if (!loopback) {\n    throw new Error('OpenRouter OAuth requires the desktop app. Use an API key instead.');\n  }\n\n  if (signal?.aborted) {\n    await loopback.cancel();\n    throw new Error('OpenRouter OAuth was cancelled.');\n  }\n\n  const verifier = randomVerifier();\n  const challenge = await createCodeChallenge(verifier);\n  const authUrl = new URL(OPENROUTER_AUTH_URL);\n  authUrl.searchParams.set('callback_url', toOpenRouterCallbackUrl(loopback.redirectUri));\n  authUrl.searchParams.set('code_challenge', challenge);\n  authUrl.searchParams.set('code_challenge_method', PKCE_METHOD);\n\n  try {\n    await openExternalUrl(authUrl.toString());\n    const callbackUrl = await Promise.race([\n      loopback.awaitCallback(),\n      new Promise<string>((_, reject) => {\n        if (!signal) return;\n        const onAbort = () => {\n          signal.removeEventListener('abort', onAbort);\n          reject(new Error('OpenRouter OAuth was cancelled.'));","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/utils/openrouterOAuth.ts#L113-L149","documentation":"connectOpenRouterViaOAuth accepts an optional AbortSignal via deps.signal. Right after the loopback listener starts, the function checks signal?.aborted; if the caller already cancelled (dialog closed, component unmounted, timeout), it cancels the listener and throws this cooperative-cancellation error before the browser window is ever opened.","triggerScenarios":"Passing deps.signal from an AbortController that aborted during listener startup: React effect cleanup ran, the settings modal closed, a race between a Cancel button and Connect, or React 18 StrictMode double-mount aborting the first attempt.","commonSituations":"User clicks Connect then immediately closes the dialog; StrictMode mounts/unmounts the connecting component in dev; a wrapper enforces a timeout via AbortSignal.timeout() that expires quickly.","solutions":["Treat it as cancellation, not a failure: catch it and return quietly without setting error state.","Create a fresh AbortController per connect attempt instead of reusing a possibly-aborted one.","Check controller.signal.aborted before invoking if prior cancellation is expected.","If it fires unexpectedly, audit every .abort() call site (cleanup functions, timeout wrappers, dialog close handlers)."],"exampleFix":"// before\ntry { await connectOpenRouterViaOAuth({ signal }); } catch (e) { setError(e); }\n// after\ntry { await connectOpenRouterViaOAuth({ signal }); } catch (e) {\n  if (e instanceof Error && e.message === 'OpenRouter OAuth was cancelled.') return; // user cancel, not an error\n  setError(e);\n}","handlingStrategy":"try-catch","validationCode":"if (controller.signal.aborted) {\n  return; // skip the call entirely — nothing to cancel\n}","typeGuard":null,"tryCatchPattern":"try {\n  await connectOpenRouterViaOAuth({ signal: controller.signal });\n} catch (err) {\n  if (err instanceof Error && err.message === 'OpenRouter OAuth was cancelled.') {\n    return; // cooperative cancel — no error UI\n  }\n  throw err;\n}","preventionTips":["Abort only on real cancel intents (unmount, explicit Cancel) and swallow the matching cancellation error.","Use one fresh AbortController per connect attempt; never reuse an aborted controller.","Render cancellation as info (dialog closed), not as an error toast."],"tags":["oauth","cancellation","abortsignal","openrouter"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}