Skip to content

Don't crash the prepare flow when the browser closes during Google login detection#64

Merged
hynek-urban merged 1 commit into
devfrom
bowei/fix-google-prepare-unhandled-rejection
May 14, 2026
Merged

Don't crash the prepare flow when the browser closes during Google login detection#64
hynek-urban merged 1 commit into
devfrom
bowei/fix-google-prepare-unhandled-rejection

Conversation

@boweiliu
Copy link
Copy Markdown
Contributor

@boweiliu boweiliu commented May 5, 2026

Why the change was needed

auth browser-prepare google-* listens for console.cloud.google.com responses to detect when the user is past the Google sign-in page. The handler asynchronously reads each response body via response.text(), but does not attach a .catch(). When the page or context closes while a body read is still in flight — either because the automation navigates onward (which the flow does many times during project creation, API enabling, and OAuth-client creation) or because the user closes the browser window — Node sees an unhandled rejection and terminates the entire prepare process.

In practice this kills prepare() mid-flight, which means any GCP project and OAuth client it has already created in the user's Google account get orphaned (latchkey only persists the credentials after prepare() returns successfully).

How this change fixes it

Attach a narrowly-scoped .catch() to the fire-and-forget response.text() chain that swallows only closed-page rejections — i.e. the ones already classified by the existing isBrowserClosedError helper (matches target closed, browser/context/page has been closed, net::ERR_ABORTED). Any other rejection is rethrown, so genuinely unexpected errors still surface. The login detector already treats a response it can't classify as inconclusive (same as not seeing it at all), so this is behaviour-preserving on the happy path. The only thing that changes is the failure mode for the closed-page race: instead of crashing the process, the polling loop in waitForGoogleLogin keeps running until login is detected or the existing timeout fires.

Caveats

The crash was observed on WSL2 (Ubuntu 22.04, Node 24, Playwright Chromium 145). It has not been reproduced on macOS, so the timing of the race may differ there.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vet found 0 issues.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vet found 0 issues.

The login detector for `auth browser-prepare google-*` listens for
console.cloud.google.com responses and asynchronously reads each
response body to decide whether the user is past the sign-in page.
That `response.text()` promise has no `.catch()` handler, so when the
page or context closes while a body is still being read (e.g. the
automation navigates onward, or the user closes the browser window),
Node sees an unhandled rejection and terminates the entire prepare
process — at which point any GCP project / OAuth client already
created by the flow is orphaned in the user's account because
latchkey never reaches the point where it persists the credentials.

Add a `.catch()` that swallows the closed-page rejection. The login
detector treats a response we couldn't read as inconclusive (same as
not seeing it at all), so this is behaviour-preserving for the happy
path and only changes the failure mode for the closed-page race from
"crash the process" to "keep polling until login is detected or the
flow times out".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@boweiliu boweiliu force-pushed the bowei/fix-google-prepare-unhandled-rejection branch from d02bba2 to 24e3c5c Compare May 5, 2026 17:18
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vet found 0 issues.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vet found 0 issues.

@weishi-imbue
Copy link
Copy Markdown
Contributor

Reproduced this on macOS today — same uncaught rejection signature, same stack:

response.text: Protocol error (Network.getResponseBody): No resource with given identifier found
  at checkGoogleLoginResponse (src/services/google/base.ts:339)
  at _Page.responseHandler (src/services/google/base.ts:362)

Environment: macOS Darwin 25.4.0, Node v22.22.0, latchkey 2.10.1 (driven from minds.app's permission grant flow, which runs auth browser-prepare google-gmail then auth browser google-gmail). Same symptom as you described — crash during the prepare flow, GCP project got orphaned because credentials never persisted.

This matches your WSL2 trace exactly, so it doesn't look macOS-specific — just timing-dependent.

A second prepare attempt against a fresh state hit a different failure mode (waitForURL 16s timeout in createProject after the submit click; project apparently created but redirect hadn't propagated to Playwright yet). Out of scope here — flagging it just so it's on someone's radar.

The narrow-scope catch on isBrowserClosedError looks right to me — broader swallowing would hide real bugs. +1 to merge.

@hynek-urban hynek-urban changed the base branch from main to dev May 14, 2026 07:27
@hynek-urban
Copy link
Copy Markdown
Collaborator

@boweiliu @weishi-imbue Thank you!

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vet found 0 issues.

@hynek-urban hynek-urban merged commit da4c049 into dev May 14, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants