{"record":{"id":"cf0afd097b0fed2c","repo":"gitbutlerapp/gitbutler","slug":"the-browser-did-not-finish-signing-in","errorCode":null,"errorMessage":"The browser did not finish signing in","messagePattern":"The browser did not finish signing in","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/lite/ui/src/routes/project/$id/workspace/Settings/Account.tsx","lineNumber":59,"sourceCode":"\t\tsetSigningIn(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst login = await window.lite.getLoginToken();\n\t\t\t// Names the client for the login page, as apps/desktop does with its\n\t\t\t// build type. Without it the page can only offer a token to copy.\n\t\t\tconst url = new URL(login.url);\n\t\t\turl.searchParams.set(\"bt\", \"release\");\n\t\t\tawait window.lite.openInWebBrowser(url.toString());\n\t\t\t// The page sends the account back over `but://login`, which the main\n\t\t\t// process persists, so this waits for the account to appear rather\n\t\t\t// than for a reply of its own.\n\t\t\tawait pollUntilSuccess({\n\t\t\t\tattempt: async () => {\n\t\t\t\t\t// Signed out reads as `null` rather than an error, which would end\n\t\t\t\t\t// the poll on its first try. The message is only ever seen if the\n\t\t\t\t\t// poll then runs out of time, so it reads as the failure.\n\t\t\t\t\tconst profile = await window.lite.getUserProfileLocal();\n\t\t\t\t\tif (profile === null) throw new Error(\"The browser did not finish signing in\");\n\t\t\t\t\treturn profile;\n\t\t\t\t},\n\t\t\t\tintervalMs: pollIntervalMs,\n\t\t\t\ttimeoutMs: pollTimeoutMs,\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\t\t\tawait client.invalidateQueries({ queryKey: userProfileQueryOptions.queryKey });\n\t\t\tawait client.invalidateQueries({ queryKey: aiConfigurationQueryOptions.queryKey });\n\t\t} catch (caught) {\n\t\t\t// Abandoning is not a failure to report.\n\t\t\tif (!controller.signal.aborted) setError(errorMessageForToast(caught));\n\t\t} finally {\n\t\t\tif (!controller.signal.aborted) setSigningIn(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<Section>","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/lite/ui/src/routes/project/$id/workspace/Settings/Account.tsx#L41-L77","documentation":"Thrown inside the Lite app's sign-in flow as the poll's per-attempt failure: after opening the browser at the login URL (with bt=release param), the code polls window.lite.getUserProfileLocal() and treats null as 'not signed in yet'. The message only becomes user-visible when pollUntilSuccess exhausts its timeout, meaning the but://login round trip never persisted an account.","triggerScenarios":"User closed the browser tab without completing sign-in; the but:// deep link did not reach the main process (handler not registered / OS protocol association broken); the main process persisted the account but the local-profile read path keeps returning null.","commonSituations":"Default browser blocks custom protocol redirects; Lite app installed without but:// registration; slow SSO where sign-in takes longer than pollTimeoutMs; signed into a different account so the expected profile never appears.","solutions":["Retry sign-in and complete it fully in the opened browser tab.","Check the OS registers the but:// protocol with the Lite app (reinstall or repair protocol registration if not).","Raise pollTimeoutMs for slower SSO flows so legitimate sign-ins are not cut off.","Inspect main-process logs for the login deep-link handler to confirm the account was received and persisted."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const profile = await window.lite.getUserProfileLocal();\nif (profile === null) {\n  setError(\"Not signed in yet — start sign-in from Account settings\");\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await pollUntilSuccess({ attempt, intervalMs: pollIntervalMs, timeoutMs: pollTimeoutMs, signal: controller.signal });\n} catch (caught) {\n  if (!controller.signal.aborted) {\n    setError(caught instanceof Error && caught.message.includes(\"did not finish signing in\")\n      ? \"Sign-in timed out — try again and complete it in the browser\"\n      : errorMessageForToast(caught));\n  }\n}","preventionTips":["Size pollTimeoutMs for realistic SSO latency (60s+) so slow logins are not reported as failures.","Verify but:// protocol registration during app install so the callback can reach the main process.","Watch main-process logs for the login deep link when polls time out."],"tags":["gitbutler","lite","authentication","oauth","polling"],"backgroundTag":"oauth-callback-timeout","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}