Skip to content

fix(cli): capture takes a refused screenshot once more before naming it - #7222

Merged
miguel-heygen merged 3 commits into
mainfrom
fix/capture-retry-refused-shot
Nov 5, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
fix/capture-retry-refused-shot

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

What

capture now takes a refused screenshot once more before giving up on it, as snapshot already does. A shot that is refused twice is still named in the JSON warnings (could not capture screenshots/scroll-055.png (scrolled to 3780px): ...), and the capture goes on to the next tiles and the full-page image.

Why

A Mac report: capture <site> --skip-vision got 13 of 21 screenshots and no full-page image, each missing one refused with Protocol error (Page.captureScreenshot): Unable to capture screenshot. The engine already counts that message as a passing browser error (isTransientBrowserError), and snapshot retries it once; capture skipped the shot on the first refusal.

This is a mitigation. The root cause is still being looked for: 15 captures of 5 long public pages on a GitHub macos-latest arm64 runner (0.9.13, Node 24) had no refusal at all, and Linux had none either.

Related work

None open on this path.

Size

A lone fix: no open PR changes capture's screenshots, so nothing else can carry it.

How

  • screenshotOnceMore moves from commands/snapshot.ts to utils/screenshotOnceMore.ts, unchanged, and its test moves with it. snapshot imports it from there.
  • captureScrollScreenshots and captureFullPagePlate call page.screenshot through it. Only a passing refusal is retried; any other error fails at once, as before. The retry reports the first error if the second try fails too.

Test plan

  • Unit tests added/updated

  • Manual testing performed

  • Documentation updated (if applicable)

  • Comments follow CONTRIBUTING.md "Comments": they say why, not what, and a bug fix says what the code must do and how to reproduce the bug

  • New: takes again, once, a tile Chrome refused, so a passing refusal costs no shot. One refusal, and every tile and the plate are written with no failure.

  • Changed: the named-failure tests in screenshotCapture.test.ts and coreExtractionPhase.test.ts now refuse the same tile twice, since one refusal no longer loses it.

  • fails without the fix: both tests fail on main's screenshotCapture.ts and pass here.

  • Every capture/ test and screenshotOnceMore.test.ts (629 passed) 3 runs in a row, and every commands/snapshot* unit test (224 passed), on Linux. oxfmt and oxlint are clean on the changed files.

  • Not done: a real capture with a refusal, because no machine I ran on reproduces one. The retry runs only on that error, so pages that capture fine today take the same path.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes at 5a9096ec. The one blocker is CI; the change itself looks right.

Blocker: the Comments workflow fails at this head (comment-ratchet, merge-base d452a6fad):

NEW file over its package's comment share: packages/cli/src/utils/screenshotOnceMore.ts is 13.3% (2/15), the package is 9.7%

The function was moved without comments, and this PR added a new two-line JSDoc to it. Cutting that to one line (1/14 = 7.1%) passes, for example /** Retries once on a transient Chrome refusal; a second failure rethrows the first error. */. Dropping it also works, since the function name already says most of it.

What I checked

  • Fails-without-the-fix line: present and accurate. With only capture/screenshotCapture.ts reverted to main, both named tests fail (takes again, once, a tile Chrome refused… and names the tile Chrome refused twice…, 2 failed / 31 passed). With the PR, they pass.
  • Local run passes: screenshotCapture, screenshotOnceMore and every commands/snapshot* unit test (14 files, 224 passed), matching the body.
  • Move: the move is clean. snapshot.ts was the only importer, and snapshot.test.ts's source-string assertion still matches. capture/ already imports @hyperframes/engine statically (assetDownloader.ts), so the new import adds no load cost.
  • Behaviour: a non-transient error still fails on the first try, and a double refusal still lands in failures under its tile name.

Non-blocking

  • isTransientBrowserError also matches Target closed, Session closed and browser has disconnected. Once the browser is gone, every remaining tile now makes two quick failing calls instead of one. That's harmless, and snapshot already behaves the same way.

Other CI at this head: PR captures, regression and Player perf are green. CI, CodeQL, preview-regression and Windows render verification were still running.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving at ca3253a2.

The only change since my review at 5a9096ec is the doc comment in packages/cli/src/utils/screenshotOnceMore.ts, now one line: 1 insertion, 2 deletions, no code changed. That was the sole blocker, and the Comments workflow now passes at this head. Everything else from the earlier review carries over unchanged: the fails-without-the-fix line is verified (both named tests fail on main's screenshotCapture.ts), and the 224 local unit tests pass.

CI at this head: Comments, regression, PR captures, Player perf and preview-regression are green. CI, CodeQL and Windows render verification were still running at approval, and their runs on the previous head were cancelled by this push. Those checks still gate the merge.

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 267 (base branch 267), smooth 114 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

Unstable (1)

  • seqrotatenudge-none-pct-r0-root-z100: tracking 0.03, pressJump 0, drop 0, reload 3.03, render 0.04, renderKey -, undo false, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.08, renderKey -, undo true, teleport true / tracking 0.03, pressJump 0, drop 0, reload 0, render 0.08, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving at 7e7e5ffa.

What changed since my approval at ca3253a2: the only change is in packages/cli/src/capture/coreExtractionPhase.test.ts, a test file; the production code is the same as at the approved head. At ca3253a2, CI failed on Linux (Test (cli 1/2)) and on Windows (engine-cli-1). Both failures were the same test: core extraction screenshot budget > names the screenshot Chrome refused and keeps the rest. It refused a single call, and that call is now retried and succeeds. The test now refuses calls 2 and 3 (the first try and the retry for scroll-100.png) and is renamed to "…refused twice…". It still asserts the same named warning and that the other shots are kept. This mirrors the screenshotCapture.test.ts change already in the PR.

I missed this test in my earlier local run because I only ran screenshotCapture, screenshotOnceMore and snapshot*. This time I ran the whole src/capture unit suite on this head: 55 files, 625 tests passed.

CI at this head: CI passed, including Test (cli 1/2) and (cli 2/2). Windows render verification passed, including engine-cli-1 and engine-cli-2. Comments, CodeQL, regression, Player perf, preview-regression and PR captures are also green.

@miguel-heygen
miguel-heygen added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit ada71da Nov 5, 2026
130 of 131 checks passed
@miguel-heygen
miguel-heygen deleted the fix/capture-retry-refused-shot branch November 5, 2026 08:37
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.

1 participant