{"record":{"id":"59954eac4c5192c4","repo":"tinyhumansai/openhuman","slug":"timed-out-waiting-for-browser-sign-in-try-again","errorCode":null,"errorMessage":"Timed out waiting for browser sign-in. Try again.","messagePattern":"Timed out waiting for browser sign-in\\. Try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"app/src/components/channels/mcp/ConnectAuthModal.tsx","lineNumber":297,"sourceCode":"    setBusy(true);\n    setError(null);\n    setOauthWaiting(true);\n    void (async () => {\n      try {\n        const url = await mcpClientsApi.oauthBegin(server.server_id);\n        await openUrl(url);\n        const started = Date.now();\n        const poll = async (): Promise<void> => {\n          const statuses = await mcpClientsApi.status();\n          const mine = statuses.find(s => s.server_id === server.server_id);\n          if (mine?.status === 'connected') {\n            const result = await mcpClientsApi.connect(server.server_id);\n            onConnected(result.tools ?? []);\n            onClose();\n            return;\n          }\n          if (Date.now() - started > 180000) {\n            throw new Error(t('mcp.connectAuth.oauthTimeout'));\n          }\n          window.setTimeout(() => {\n            void poll().catch(handlePollError);\n          }, 2500);\n        };\n        const handlePollError = (err: unknown) => {\n          setError(err instanceof Error ? err.message : String(err));\n          setOauthWaiting(false);\n          setBusy(false);\n        };\n        await poll().catch(handlePollError);\n      } catch (err) {\n        const msg = err instanceof Error ? err.message : String(err);\n        log('oauth failed: %s', msg);\n        setError(msg);\n        setOauthWaiting(false);\n        setBusy(false);\n      }","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/components/channels/mcp/ConnectAuthModal.tsx#L279-L315","documentation":"OAuth connect flow for an MCP server: ConnectAuthModal opens the auth URL in the system browser (openUrl), then polls mcpClientsApi.status() every 2.5s looking for the server's status to become 'connected'. If 180000 ms (3 minutes) elapse first, it throws the localized string behind 'mcp.connectAuth.oauthTimeout' and resets the waiting/busy modal state so the user can retry.","triggerScenarios":"The user never completes (or abandons) the browser sign-in within 3 minutes; or sign-in completes but the server never reaches status 'connected' because the token exchange or redirect failed server-side, so every poll returns a non-connected status until the deadline.","commonSituations":"User closes the browser tab mid-flow; popup/redirect blocked or corporate proxy strips the callback; backend OAuth exchange fails silently while the modal keeps polling; very slow identity providers or users leaving the modal open in the background.","solutions":["Click Connect again and complete the browser sign-in promptly - most cases are simply an abandoned browser step","If sign-in did complete, check the server's status directly (mcpClientsApi.status() / MCP health tab) - a terminal 'error' status means the exchange failed, not that you were too slow","Verify the backend OAuth callback reached the server (proxy/VPN blocking the redirect is common)","Only if a legitimate flow consistently needs more than 3 minutes, raise the 180000 constant"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Fail fast if the server is already in a terminal state before opening the browser\nconst statuses = await mcpClientsApi.status();\nconst mine = statuses.find(s => s.server_id === server.server_id);\nif (mine && mine.status !== 'disconnected' && mine.status !== 'error') {\n  // already connected or connecting - no OAuth round-trip needed\n}","typeGuard":null,"tryCatchPattern":"// distinguish the timeout from transport failures in the poll error handler\nconst handlePollError = (err: unknown) => {\n  const msg = err instanceof Error ? err.message : String(err);\n  if (msg === t('mcp.connectAuth.oauthTimeout')) {\n    setError(msg);          // user-abandonable: offer Connect again\n    setOauthWaiting(false); // keep the modal open for retry\n  } else {\n    setError(msg);\n    setOauthWaiting(false);\n  }\n  setBusy(false);\n};","preventionTips":["Complete the browser sign-in promptly after Connect - the window is 3 minutes at a 2.5s poll interval","If sign-in completed but the modal still times out, check mcpClientsApi.status() for a terminal error instead of retrying blindly","Keep the modal mounted while polling; unmounting abandons the loop without cancelling the server-side flow"],"tags":["mcp","oauth","browser-auth","polling","timeout","i18n"],"backgroundTag":"oauth-polling-timeout","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}