fix(capture): bound scroll/evaluate timeouts so heavy pages keep capturing (#3236)

* fix(capture): bound scroll/evaluate timeouts so heavy pages keep capturing

Fonts/Vercel-class sites were failing after navigation when a single in-page
scroll/evaluate hung until protocolTimeout. Drive lazy scroll from Node,
degrade on evaluate timeouts, and stop labeling those failures as bot blocks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(capture): enforce stage budgets and surface evaluate degradation

Bound each scroll/content evaluate with remaining stage time so a wedged
page.evaluate cannot outlive the advertised 15s/8s budgets, skip recovery
CDP calls after expiry, and return/propagate timed-out animation and
screenshot work so caller warnings are reachable.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(capture): prefer TimeoutError instanceof for timeout classification

Use puppeteer-core TimeoutError as the primary signal for navigation vs
evaluate/protocol timeouts, with message checks only as a fallback for
string formatting and non-TimeoutError cases.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xuanru Li
2026-08-11 18:26:22 -07:00
committed by GitHub
co-authored by Cursor
parent 93fbb6d7fd
commit 43dba22057
12 changed files with 621 additions and 106 deletions
+2 -5
View File
@@ -250,14 +250,11 @@ export default defineCommand({
}
} catch (err) {
const errMsg = normalizeErrorMessage(err);
// Write BLOCKED.md so the user/agent knows the capture failed
try {
const { mkdirSync, writeFileSync } = await import("node:fs");
const { formatCaptureFailureReason } = await import("../capture/captureTimeout.js");
mkdirSync(outputDir, { recursive: true });
const isTimeout = /timeout|timed out/i.test(errMsg);
const reason = isTimeout
? "Page navigation timed out — the site may be blocking headless browsers or requires authentication."
: `Capture failed: ${errMsg}`;
const reason = formatCaptureFailureReason(errMsg);
writeFileSync(
`${outputDir}/BLOCKED.md`,
`# Capture Failed\n\n${reason}\n\nURL: ${url}\n\n## What to try\n\n- Re-run with a longer timeout: \`--timeout 60000\`\n- The site may block headless browsers (anti-bot protection)\n- Try capturing a different page on the same domain\n`,