Skip to content

fix(errors,migrated): stop reporting two things we never observed (#803, #789) - #876

Merged
ffroliva merged 1 commit into
developfrom
chore/housekeeping-doc-accuracy
Sep 18, 2026
Merged

ffroliva merged 1 commit into
developfrom
chore/housekeeping-doc-accuracy

Conversation

@ffroliva

@ffroliva ffroliva commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

Summary

Two bugs of one shape — gflow asserting something no code path measured, on the exact field a user checks to find out what went wrong — plus the CI gap that let their neighbours' regression tests run nowhere.

#803 — auth errors blamed a cookie the route never reads

AisandboxAuthError's default remediation read "SAPISID cookie missing, expired, or unreadable. Re-run gflow auth login --profile <name> and retry."

v0.74.0 corrected the two gflow credits sites. The other three inherited the default, so every route through _run_with_aisandbox_retry still said it — createScene, commitWorkflow, updateSceneWorkflows, getSceneWorkflows, createEntity, patchEntity, projectInitialData, batchDeleteAssets, upsampleImage, the concat/status routes.

None of them reads SAPISID. The credential is a Bearer token minted by labs' /fx/api/auth/session; SAPISID's only role is getting that endpoint to answer at all. By the time any of these three raise, SAPISID has demonstrably just worked.

The advice was not merely wrong — it was expensive. On a profile with no browser-strategy marker, a failed re-login rolls the marker back and starts the #791 loop, so the remediation could cost the user the working session they still had.

Now each site says what was actually rejected:

Site Condition What it says now
client.py _fetch_access_token body is not JSON an interstitial or redirect, not an expired cookie; re-authenticating cannot change a non-JSON reply
same, no access_token labs answered 200 with no token the flow.google.com shape (#795); re-authenticating will not help and can roll the marker back
_run_with_aisandbox_retry 401 after refresh the token was minted and refused on route {route} — the sign-in is the one thing proven working
class default anything else no SAPISID claim at all

KNOWN_ISSUES.md records that v0.74.0 covered only the credits half.

#789 — the migrated host reported a model it never observed

model_name_type echoed the requested --model back on flow.google.com. The ogiZ0b reply carries no model field: 2026-09-08-migrated-image-submit-wire.md enumerates the entire payload — media id, workflow id, seed, prompt, signed URL, dimensions, sibling workflow record — and there is none. (GEM_PIX_2 in that spike is the settings-pane DOM, i.e. what was selected, not what the reply returned.)

recorder.py persists the value as AssetRecord.model, so gflow data read the echo back as though Flow had confirmed it. And with a hidden model picker (#788) the model actually selected can differ from the one requested — which makes the echo not merely unverified but wrong on the single field that would reveal it.

Now None on that host, typed str | None, documented in USAGE. labs.google is untouched: there the value comes from Flow's own modelNameType.

The request echo is unaffected — the envelope's top-level "model": "NARWHAL" still reports what you asked for. What is gone is the claim that Flow agreed.

CI ran zero e2e tests

Five files under tests/e2e/ are hermetic: every Flow origin is served by page.route(...).fulfill(...), so they need no account, no profile, no network and no credits — only the Chromium the test job already installs for the real-DOM fixture tests.

They are the regression tests for #593, #773, #859 and #860. pyproject.toml's addopts excludes -m e2e and no workflow overrode it, so they ran nowhere: a regression of any of those four fixes would have gone green. Now wired in behind the same python-version == '3.13' condition as the Chromium install, with a count guard so an empty run cannot pass as a green one.

27 tests, ~4 min locally. The other 42 e2e files genuinely need a logged-in profile and stay excluded.

Also

AGENTS.md described gflow docs as (list/print/search …), which reads as subcommands beside its neighbours (gflow credits (user/list), gflow image (t2i/i2i/…)). gflow docs list errors — the real shape is bare / <topic> / --search TERM.

Verification

#789 — live, five layers, $0 (Flow's daily image quota, no credits):

  • pytest -m e2e_image tests/e2e/test_migrated_host_e2e.py on a live flow.google.com profile → 2 passed in 86s: test_e2e_t2i_runs_on_a_moved_account and test_e2e_mcp_i2i_runs_on_the_migrated_host. Both surfaces, CLI and the MCP twin.
  • Real gflow image t2i --json on that profile:
    "model_name_type": null,
    "media_name": "4814faa7-caf6-4a3f-857e-3699d82b00fd",
    "aspect_ratio": "IMAGE_ASPECT_RATIO_PORTRAIT",
    "dimensions": {"width": 768, "height": 1376},
    "is_signed_url": true
    Every other field populated — which is what makes the null evidence rather than an empty envelope. (My first read of this was flat rather than into images[] and returned None for everything; that was an instrument error, not a result.)
  • Artifact on disk: 443,433 bytes, magic ff d8 ff e0 00 10 4a 46 49 46 (JPEG/JFIF), catalog row present via gflow data media.

#803 — offline only, and here is the blocker. The three raise sites need Flow to answer with a non-JSON body or a token-less session on an aisandbox route. Attempting to reach one live (gflow character list on a migrated profile) never got there: the labs tRPC route answers 404 "Flow RPCs have been deprecated and disabled" first, so _fetch_access_token is not called. I could not force the condition on the profiles available here. Covered by 4 new unit tests plus the existing test_credits_http.py suite (78 passed); the live leg needs a profile in the state that produces it.

That attempt did surface a new misleading error of the same class, filed with its capture as #875 rather than fixed here — a fix discovered ten minutes before a release gets less scrutiny than the code it patches.

Gates: ruff check · ruff format --check · repo hygiene (1167 files) · doc links (197 files) · website PII · website mirror in sync · council memory · pyright src (0 errors) — all green. Unit suite 1775 passed, 3 skipped.

Closes #803
Closes #789

Summary by CodeRabbit

  • Bug Fixes

    • Improved authentication error messages with clearer token guidance and the specific route rejected after refresh.
    • Corrected handling of sessions that return unexpected data or omit access tokens.
    • Image results from Flow now report an unknown model as null instead of echoing the requested model.
  • Documentation

    • Updated command usage and clarified model reporting differences between Flow and Labs hosts.
    • Refined known-issues guidance for migrated-host authentication failures.
  • Tests

    • Expanded authentication and image-generation regression coverage, including hermetic browser tests.

…, #789)

Both are the same failure shape: gflow asserting something no code path
measured, on the exact field a user would check to find out what went wrong.

#803 — AisandboxAuthError's default remediation read "SAPISID cookie missing,
expired, or unreadable. Re-run `gflow auth login`". v0.74.0 fixed the two
`gflow credits` sites; the other three inherited the default, so every route
through `_run_with_aisandbox_retry` — createScene, commitWorkflow,
createEntity, projectInitialData, upsampleImage and the rest — still said it.
None of them reads SAPISID: the credential is a Bearer token minted by labs'
session endpoint, and SAPISID's only job is getting that endpoint to answer at
all. By the time any of these raise, SAPISID is demonstrably working.

The advice was not merely wrong, it was expensive. On a profile with no
browser-strategy marker a failed re-login rolls the marker back and starts the
#791 loop, so the remediation could cost the user the working session they
still had.

Each site now says what was actually rejected: a non-JSON reply is named as an
interstitial or redirect, a token-less session is named as the flow.google.com
shape, and a 401 after refresh names the route that refused it. The class
default no longer asserts a SAPISID failure at all. KNOWN_ISSUES records that
v0.74.0 covered only the credits half.

#789 — model_name_type echoed the requested --model back on flow.google.com.
The ogiZ0b reply carries no model field; the 2026-09-08 wire spike enumerates
the whole payload and there is none. recorder.py persists that echo as
AssetRecord.model, so `gflow data` read it back as though Flow had confirmed
it. With a hidden model picker (#788) the model actually selected can differ
from the one requested, which makes the echo not just unverified but wrong on
the one field that would reveal it. Now None on that host, typed str | None,
documented in USAGE. labs.google is unchanged — there the value is Flow's own.

Also here:

- CI runs the five hermetic e2e files. Every Flow origin in them is served by
  page.route().fulfill(), so they need no account, no profile, no network and
  no credits — only the Chromium the test job already installs. They are the
  regression tests for #593, #773, #859 and #860, and because pyproject's
  addopts exclude `-m e2e` and no job overrode it, they ran nowhere: a
  regression of any of those four would have gone green. 27 tests, ~4 min,
  with a count guard so an empty run cannot pass as a green one.
- AGENTS.md described `gflow docs` as "(list/print/search ...)", which reads as
  subcommands beside its neighbours; `gflow docs list` errors. Says the real
  shape now.
@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 491c5aca-6674-4a06-8ed1-473945f78f81

📥 Commits

Reviewing files that changed from the base of the PR and between 55feac4 and 2c8c087.

📒 Files selected for processing (14)
  • .github/workflows/ci.yml
  • AGENTS.md
  • CHANGELOG.md
  • KNOWN_ISSUES.md
  • docs/USAGE.md
  • src/gflow_cli/api/client.py
  • src/gflow_cli/api/dto.py
  • src/gflow_cli/api/transports/migrated_composer.py
  • src/gflow_cli/errors.py
  • tests/api/test_aisandbox_auth_error.py
  • tests/api/test_aisandbox_auth_remediation.py
  • tests/api/transports/test_migrated_composer.py
  • website/docs/KNOWN_ISSUES.md
  • website/docs/USAGE.md
 ____________________________________
< Squash the bug, not the messenger. >
 ------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@ffroliva
ffroliva merged commit 4ada6c0 into develop Sep 18, 2026
18 of 19 checks passed
@ffroliva
ffroliva deleted the chore/housekeeping-doc-accuracy branch September 18, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant