Skip to content

Incident bundles on the migrated video path are captured after the page is parked, so they ship an all-zero DOM and a blank screenshot #722

Closed

Description

@ffroliva

Summary

On the migrated host's video path, the incident bundle is captured after the pooled page has been parked at about:blank. So every such bundle ships:

"tag_counts": {"div": 0, "button": 0, "input": 0, "textarea": 0,
               "dialog": 0, "iframe": 0, "video": 0, "img": 0},
"ligatures": [], "ligature_count": 0, "overlays": [],
"title": {"category": "other", "length": 0},
"url": {"host_category": "other", "route": "other"}

and a blank white screenshot.

Why this is worse than a missing artifact

The bundle is not empty, it is confidently empty. It looks like a successful capture of a dead page, so a reader concludes "the page was gone" — when the truth is "we blanked it before looking". A missing artifact prompts you to go and measure. A zeroed one answers the question, wrongly, and stops the investigation.

This is the same failure family the project has been correcting all week: a measurement that could not be taken, recorded as a measurement of absence. Here the diagnostics themselves manufacture it, on every incident.

It has already cost. #692's reporter attached a bundle with ligature_count: 0 and every element count at zero, and that was read as evidence about the migrated DOM. It was evidence about capture ordering.

Mechanism

_generate_video_locked wraps the migrated composer call in a try/finally, and the finally parks the pooled page:

finally:
    # The pooled page would otherwise stay on flow.google.com/project/<id>, and the
    # NEXT request on this client would be routed by that URL instead of by its own shape
    await page.goto("about:blank", wait_until="commit", timeout=5_000)

The park is correct and should stay — it prevents the next request being routed by a stale URL. The problem is ordering: incident capture happens at the CLI layer (run_with_handlers), which is outside the transport, so it runs after the finally has already blanked the page it is about to photograph.

Measured today

Two UiSelectorDriftError incidents from gflow video t2v on the migrated host, both with all element counts zero and a blank screenshot:

  • 20260907T101449Z-3beb72d8-…
  • 20260907T101604Z-d3f87d23-…

Both were undiagnosable from their bundles. The actual cause — the account was out of Veo credits, and Flow had replaced the submit control with a prompt-warning-button (#721) — was only findable by writing a probe that inspects the page while it is still alive. The information was on screen at failure time and the bundle threw it away.

Suggested fix

Capture the page state before the teardown runs, not after. Options, roughly in order of cost:

  1. Snapshot inside the transport at the point of failure and attach it to the raised error, so the CLI-layer bundle writes a snapshot taken while the page was live.
  2. Have the finally skip the park when an exception is propagating, and let the incident capture park it afterwards.
  3. Failing both, make the capture detect about:blank / an all-zero tally and record capture_status: "post_teardown" instead of "complete" — so at minimum the bundle stops asserting a blank page as a finding.

(3) alone would have prevented today's dead end and is the smallest change; (1) is the one that actually preserves the evidence.

Not measured

Whether the same ordering affects the image path or the labs video path. I only observed it on the migrated video path, twice. Worth checking the other finally blocks that park or close a page before assuming it is unique to this one.

Refs #721, #719, #692

Activity

  1. ffroliva commented on Sep 7, 2026

    @ffroliva
    OwnerAuthor

    Third independent instance, and this bundle carries a discriminator the all-zero ones do not: it proves the page moved between the failure and the capture, rather than merely being empty when captured.

    Run: tests/e2e/test_insufficient_credits_e2e.py against ci-probe, 2026-09-07 13:49 (migrated host, $0, nothing submitted).

    The failure was raised here:

    UiSelectorDriftError: migrated host: the settings trigger (.settings-trigger-button)
    did not become visible within 30s on https://flow.google.com/about (host=migrated)
    

    The bundle written for that failure records:

    "ligature_count": 0,
    "tag_counts": {"div": 0, "button": 0, "input": 0, "textarea": 0, "dialog": 0,
                   "iframe": 0, "video": 0, "img": 0},
    "title": {"length": 0},
    "url": {"host_category": "other", "route": "other"}

    host_category: "other", on a failure that occurred on flow.google.com. That field is derived from the URL at capture time. A page still sitting on https://flow.google.com/about would classify as the Flow host; "other" is what about:blank classifies as. So the URL the error reports and the URL the bundle captured are different pages — which is the mechanism this issue names, caught in the act rather than inferred from a zero count.

    Why that distinction earns a comment: an all-zero bundle is ambiguous on its own, because a genuinely dead or unrendered page also produces zeros. This one is not ambiguous. The failure happened on one URL and the evidence was gathered from another, so the zeros are not a measurement of the failing page at all.

    That is what makes the bundle actively misleading rather than merely thin. It reads as a successful capture of an empty page, and a reader draws "the DOM was gone" from something that only shows "we blanked it before looking". The #692 precedent in this issue is exactly that read: a reporter's div: 0, button: 0 was taken as evidence the labs DOM had disappeared.

    Suggested acceptance criterion, since the fix should be checkable rather than eyeballed: a regression test asserting that for a captured failure, the bundle's recorded URL equals the URL in the raised error. Cheap, mechanical, and it fails today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions