You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A /about redirect on flow.google.com is reported as UiSelectorDriftError (exit 23) #756
Measured 2026-09-08 on ci-probe while instrumenting #719. $0.
What happens
MigratedComposer.ensure_editor navigates to flow.google.com/project/<id>, the app redirects to flow.google.com/about, the settings trigger is of course never there, and 30 s later:
UiSelectorDriftError (exit 23): migrated host: the settings trigger (.settings-trigger-button)
did not become visible within 30s on https://flow.google.com/about (host=migrated)
The URL is in the message, and that is the only reason this was diagnosable at all — credit where due to whoever put it there.
Why it is wrong
/about is Flow's public landing page. Ending up there is not selector drift; it is the app telling us this session cannot open that project on this host. Reporting it as drift sends the user to "check for a newer gflow-cli release, then file a bug" over an account/session state no release will change — the same manufactured-drift-report problem #721 fixed for credit shortfalls, and #749 for agent mode. That is now three instances of one pattern, which is what makes it worth a general guard rather than a third special case.
What makes this confusing
gflow auth status --profile ci-probe reports the Flow session verified (compiledgrownth.official@gmail.com), and gflow project list --profile ci-probe works and lists the project. So every check a user would run says "you are signed in", and only the migrated editor disagrees.
Suggested shape
The check is cheap and needs no new anchor — after page.goto, the URL either is the project or it is not:
ifnotpage.url.startswith(MIGRATED_PROJECT_URL.format(project_id=project_id)):
# landed somewhere else entirely (/about, a sign-in page, a different project)
The existing flow_host_kind / goto-redirect plumbing from #639/#644 is next door, and goto returning before the client-side redirect is a trap already recorded on this path — so the check belongs where the run already blocks (the readiness wait), not in a new bounded wait after goto.
Not measured
Why ci-probe is redirected. Signed out on flow.google.com specifically, a cohort gate, or a project-access problem are all consistent with one observation. This issue is only about the reporting, not the cause — do not let a fix assert a cause it has not measured.
Whether the same redirect can happen mid-session on an account that worked a minute earlier.
Follow-up measurement — the landings are distinguishable, which makes the fix sharper than what I filed above. Four navigations, two profiles, $0:
Navigation
Lands on
ci-probe → labs.google/fx/tools/flow
accounts.google.com/…/signin/accountchooser
ci-probe → its own project
flow.google.com/about
ffroliva → its own project
flow.google.com/project/<id>
ffroliva → ci-probe's project
flow.google.com/404?reason=project
So my "Not measured" note above is now partly answered, and one guess in it was wrong:
/about = signed out. Confirmed independently by the labs root redirecting to the OAuth account chooser on the same profile.
404?reason=project = signed in, no access to that project. A different URL, so the two states this issue lumped together are separable at zero cost.
That means the fix should name three outcomes, not two:
/project/<id> -> good
/about -> this profile is signed out on flow.google.com; run `gflow auth login`
/404?reason=project -> signed in, but this account cannot open that project
anything else -> genuinely unexpected; keep the current drift message
The last row matters — this should not become a guard that swallows real drift. Anything not on the known list stays exit 23 with the URL, which is what made this diagnosable in the first place.
The confusing part now has a mechanism
gflow auth status --profile ci-probe reports the session verified, and gflow project list returns three projects, while the browser is signed out. Those are two different lanes: the REST lane rides the NextAuth token (__Secure-next-auth.session-token) and still works; the browser lane needs the Google SSO session, which is gone. Only generation uses the broken one.
So auth status is not merely unhelpful here, it is actively misleading — it checks the lane that still works. Worth deciding whether that is part of this issue or its own; I have not filed it separately.
Not measured: why the browser session died, or when.
Adding evidence from my macOS v0.71.1 failure to this issue, following the existing follow-up measurement rather than opening another landing-classification issue.
The same contradiction appears in my run:
Fresh login reported success, and the REST credits request succeeded with HTTP 200.
The Python reader logged cookie_decryption_failed_falling_back_to_playwright. The fallback browser saw the labs-scoped __Secure-next-auth.session-token, but no exact-name SAPISID in its full jar.
Generation found the existing labs session cookie, so its session-cookie seeding branch did not run.
migrated.dispatch mode=t2v selected the migrated composer, navigation reached /about, and the missing settings trigger was reported as UiSelectorDriftError (exit 23).
This adds a Mac incident to the healthy-REST/signed-out-browser pattern already measured here. Missing or invalid Google SSO in the migrated browser lane is the strongest supported explanation, but my evidence does not identify when SSO was lost. Missing SAPISID means exactly that cookie was not visible to the reader, not that every possible Google credential was absent. /about alone does not exclude every Google-side gate without the rendered state and redirect evidence.
The distinction worth preserving in diagnostics is REST session validity versus migrated browser readiness. At the analyzed source revision, credits obtains a labs-derived bearer credential and does not require the snapshot's Google-session boolean. Auth verification also validates the labs session. These are valid checks of that lane; their success is not proof that the migrated editor is ready.
This is the reverse of #644's original latent scenario: labs remains valid while browser SSO fails. It does not establish that the original labs-dead/SSO-alive scenario occurred, or justify forwarding Google cookies through the labs-only HTTP dictionary. Likewise, replacing a live readiness check with a cookie-presence assertion would not establish readiness.
The existing proposal to classify landings at the readiness/redirect-settle boundary fits this evidence. Preserve the distinction between measured sign-in/chooser and project-access failures, keep unexpected-route evidence and real editor selector failures visible, and capture the incident before parking the page. An extra arbitrary wait immediately after goto would miss the point about late client-side redirects already recorded here. Diagnostic wording alone will not repair SSO.
Mac verification still needs the same-profile comparison: healthy labs session with signed-out browser SSO should produce an actionable session diagnosis; an inaccessible project should remain distinguishable; after SSO repair, observe actual editor readiness. The cause of the Mac cookie-reader failure is a separate investigation, not proven by this landing.
I have omitted cookie values, account identifiers, profile paths, authentication URLs and the raw incident bundle. No new live run is claimed in this comment.
Reopening context rather than the issue — two measurements landed today that narrow this, both from catching a live occurrence on denon82 while working on #780.
A retry is doomed (PR #785). The stability probe this issue produced was re-run unmodified during a live occurrence, so its pre-registered reading governs: 5/5 attempts landed on /about over ~3 minutes, 31–35 s each, on the account's own project with a healthy session. retryable=False was a preserved default and both the code comment and FlowAppError's docstring said so; it is now the measured answer. Behaviour unchanged.
The cause is narrowed, not named (PR #786). An A/B against a working account on the wire:
denon82 (fails)
ci-probe (control)
document
200, no Location
200, no Location
hop to /about
338 ms
—
batchexecute calls
0
20, first at 275 ms
non-2xx from Flow
none
none
So it is not a server-side redirect, and the app decides without asking Flow anything — not called and refused, never called. There is no permissions call to point at. Account-wide rather than project-specific (a second project, 2/2). And gflow project list returns the redirecting project, so the backend grants access while the frontend declines to open it.
The signal is therefore present at page load rather than fetched. Which signal is not named: that means reading the document's embedded bootstrap payload, which is where bearer tokens and account data live on this origin, and it was not read. Your warning here stands — a lead is not a cause.
What would settle it: a HAR capture of the failing document through scripts/dev/har-spike/, diffed against a working one, with extract_har_summary.py producing the shareable summary.
Practical takeaway for whoever picks this up: looking for a refused permissions call is a dead end.
Activity
spike(migrated): /about is signed-out, and 404?reason=project is no-a…
ffroliva commentedon Sep 8, 2026
Follow-up measurement — the landings are distinguishable, which makes the fix sharper than what I filed above. Four navigations, two profiles, $0:
ci-probe→labs.google/fx/tools/flowaccounts.google.com/…/signin/accountchooserci-probe→ its own projectflow.google.com/aboutffroliva→ its own projectflow.google.com/project/<id>ffroliva→ci-probe's projectflow.google.com/404?reason=projectSo my "Not measured" note above is now partly answered, and one guess in it was wrong:
/about= signed out. Confirmed independently by the labs root redirecting to the OAuth account chooser on the same profile.404?reason=project= signed in, no access to that project. A different URL, so the two states this issue lumped together are separable at zero cost.That means the fix should name three outcomes, not two:
The last row matters — this should not become a guard that swallows real drift. Anything not on the known list stays exit 23 with the URL, which is what made this diagnosable in the first place.
The confusing part now has a mechanism
gflow auth status --profile ci-probereports the session verified, andgflow project listreturns three projects, while the browser is signed out. Those are two different lanes: the REST lane rides the NextAuth token (__Secure-next-auth.session-token) and still works; the browser lane needs the Google SSO session, which is gone. Only generation uses the broken one.So
auth statusis not merely unhelpful here, it is actively misleading — it checks the lane that still works. Worth deciding whether that is part of this issue or its own; I have not filed it separately.Not measured: why the browser session died, or when.
spike(migrated): #719 DOES reproduce — and my first write-up said it …
spike(migrated): read Flow's queue at $0, and find #719's root cause …
Mauryanx commentedon Sep 9, 2026
Adding evidence from my macOS v0.71.1 failure to this issue, following the existing follow-up measurement rather than opening another landing-classification issue.
The same contradiction appears in my run:
cookie_decryption_failed_falling_back_to_playwright. The fallback browser saw the labs-scoped__Secure-next-auth.session-token, but no exact-nameSAPISIDin its full jar.migrated.dispatch mode=t2vselected the migrated composer, navigation reached/about, and the missing settings trigger was reported asUiSelectorDriftError(exit 23).This adds a Mac incident to the healthy-REST/signed-out-browser pattern already measured here. Missing or invalid Google SSO in the migrated browser lane is the strongest supported explanation, but my evidence does not identify when SSO was lost. Missing
SAPISIDmeans exactly that cookie was not visible to the reader, not that every possible Google credential was absent./aboutalone does not exclude every Google-side gate without the rendered state and redirect evidence.The distinction worth preserving in diagnostics is REST session validity versus migrated browser readiness. At the analyzed source revision, credits obtains a labs-derived bearer credential and does not require the snapshot's Google-session boolean. Auth verification also validates the labs session. These are valid checks of that lane; their success is not proof that the migrated editor is ready.
This is the reverse of #644's original latent scenario: labs remains valid while browser SSO fails. It does not establish that the original labs-dead/SSO-alive scenario occurred, or justify forwarding Google cookies through the labs-only HTTP dictionary. Likewise, replacing a live readiness check with a cookie-presence assertion would not establish readiness.
The existing proposal to classify landings at the readiness/redirect-settle boundary fits this evidence. Preserve the distinction between measured sign-in/chooser and project-access failures, keep unexpected-route evidence and real editor selector failures visible, and capture the incident before parking the page. An extra arbitrary wait immediately after
gotowould miss the point about late client-side redirects already recorded here. Diagnostic wording alone will not repair SSO.Mac verification still needs the same-profile comparison: healthy labs session with signed-out browser SSO should produce an actionable session diagnosis; an inaccessible project should remain distinguishable; after SSO repair, observe actual editor readiness. The cause of the Mac cookie-reader failure is a separate investigation, not proven by this landing.
I have omitted cookie values, account identifiers, profile paths, authentication URLs and the raw incident bundle. No new live run is claimed in this comment.
fix(transports): name a known Flow landing instead of blaming the sel…
fix(review): apply the branch-council findings
fix(756): measure the /about retry flag, and unpin a test from termin…
ffroliva commentedon Sep 11, 2026
Reopening context rather than the issue — two measurements landed today that narrow this, both from catching a live occurrence on
denon82while working on #780.A retry is doomed (PR #785). The stability probe this issue produced was re-run unmodified during a live occurrence, so its pre-registered reading governs: 5/5 attempts landed on
/aboutover ~3 minutes, 31–35 s each, on the account's own project with a healthy session.retryable=Falsewas a preserved default and both the code comment andFlowAppError's docstring said so; it is now the measured answer. Behaviour unchanged.The cause is narrowed, not named (PR #786). An A/B against a working account on the wire:
denon82(fails)ci-probe(control)LocationLocation/aboutbatchexecutecallsSo it is not a server-side redirect, and the app decides without asking Flow anything — not called and refused, never called. There is no permissions call to point at. Account-wide rather than project-specific (a second project, 2/2). And
gflow project listreturns the redirecting project, so the backend grants access while the frontend declines to open it.The signal is therefore present at page load rather than fetched. Which signal is not named: that means reading the document's embedded bootstrap payload, which is where bearer tokens and account data live on this origin, and it was not read. Your warning here stands — a lead is not a cause.
What would settle it: a HAR capture of the failing document through
scripts/dev/har-spike/, diffed against a working one, withextract_har_summary.pyproducing the shareable summary.Practical takeaway for whoever picks this up: looking for a refused permissions call is a dead end.
https://claude.ai/code/session_01Nr4XuvvZv3PrL3pKEzkrLc
spike(756): classify every response, and prove the hop is client-side…
spike(756): the /about redirect is decided client-side, without askin…
fix(transports): name a known Flow landing instead of blaming the sel…
fix(799): close the council's findings, and measure the live controls
fix(799): name the agent-only composer instead of calling it selector…
spike(auth): RETRACT the server-attested claim — the oracle does not …
fix(transports): route Flow's unavailable screen to its own error (ex…
fix(transports): close the labs arm of the unavailable-screen gap, an…
chore(release): v0.76.0
chore(council): sweep the spikes corpus before dispatching reviewers
docs(spike): the /about CTA leads to Google's identity re-check, not …