# fix\(server\): text-only browser snapshots no longer hang on hidden tabs · PR \#21494 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/21494)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: Unavailable fork

Source branch: t3/preview-snapshot-hang

Target branch: main

Head commit: b7f5eb78d4ea0646577a07be75c962253d531197

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/21494)

[Commits](https://git.cafe/pingdotgg/t3code/pull/21494/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/21494/diffs?format=markdown)

## Description

---

## Problem

`preview_snapshot` fails after 10 seconds on a desktop tab the Browser panel isn't showing. Over a week of nightlies, 93 of 252 snapshots from one user failed this way, across 12 threads. Successful ones took 0.1 to 0.7 s. A `preview_evaluate` on the same tab right after a failure returned in 4 ms.

The snapshot always took a screenshot, even when the agent set neither `includeImage` nor `save`. That's the default, and nothing reads the image then. A hidden tab draws no frames, so `Page.captureScreenshot` never answers and the snapshot fails, though its text was ready in 50 ms.

![preview_snapshot on a hidden tab: before, the text is ready in 50 ms but the snapshot waits on the screenshot and fails at 10,008 ms; after, the screenshot isn't requested and the snapshot returns in 50 ms](https://gist.githubusercontent.com/Byntham/53bb153dfa1cd1044c18ceeacc772c60/raw/6eaff6622193226aee89ddf515488ed47965f8f1/snapshot-timeline.png)

## Change

- **Tool:** `preview_snapshot` asks the host for a screenshot only when `includeImage` or `save` is set.
- **Server browser:** without one, the snapshot skips `Page.captureScreenshot` and the screencast pause around it.
- **Contract:** `PreviewAutomationSnapshot.screenshot` is optional. Only the server browser builds snapshots and only this tool reads them, so no client changes.

Snapshots with `includeImage` or `save` on a hidden tab still fail. Making those tabs draw is #18873. The two changes touch different parts of the capture code.

## Scope and approval

Refs #16567. The [maintainer triage](https://github.com/pingdotgg/t3code/issues/16567#issuecomment-6024381240) confirms the failure: an offscreen desktop guest never answers `Page.captureScreenshot`, and the triage notes a snapshot captures even when `includeImage` is false. This fixes that for the default, image-free call only. Host eviction after timeouts (#16921) and the broker printing its own 15000 ms budget instead of the host's 10 s message are separate and not changed here.

## Verification

- A new test drives real headless Chromium through a CDP session whose `Page.captureScreenshot` never answers. The tree and refs come back, and no capture is requested. On main it fails after 10,045 ms with "The page was too busy to capture a snapshot in time."
- MCP tests check that only `includeImage` or `save` calls ask the host for a screenshot, and that a reply without one returns the text alone.
- In headless Chromium with begin-frame control, so no frames are drawn, `requestAnimationFrame` never fires and a bare `Page.captureScreenshot` stays pending. A text-only snapshot returns in 50 ms. The default one fails at 10,008 ms.
- `vp test run` on `ServerBrowserPage.test.ts`, `McpHttpServer.test.ts` and `ServerBrowser.test.ts`: 90 passed. Server and contracts typecheck, scoped lint and format pass.
- Not run in the packaged desktop app. The hidden-tab behavior in Electron comes from the fixtures in #16567.

Reviewed with GPT-6.1 Sol in Codex.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

> [!NOTE]
> Implemented by `claude-opus-5-5` with the help of `gpt-6.1-sol`

