Commit c395b87
committed
fix(crawler): bound the Playwright calls that carry no protocol timeout
`page.content()` and `page.evaluate()` are sent to the driver with no `timeout`
field. The Python client only injects one when a timeout calculator is passed
(_connection.py `_augment_params`), and the driver arms a timer only for a
truthy timeout (`progress.js`: `const deadline = timeout ? ... : 0`). So for
these two, no timer exists at all — they can end only when they get a reply or
the target closes.
What they wait on is the frame's execution-context promise, and
`frames.js _setContext(world, null)` discards the resolved promise and installs
a fresh unresolved `ManualPromise` on every `_contextDestroyed`. A page that
keeps committing navigations therefore wedges them forever.
Two consequences, both observed in production on a WordPress + Cloudflare
Turnstile site:
* `page_timeout` does not help. It reaches `page.goto` and the `wait_*` family
only. Lowering it from 80s to 30s changed nothing.
* Nothing is logged. Both call sites sit inside swallow-all `try/except`, so
the request simply stopped for 172 seconds until an external deadline fired.
Same race, two outcomes from the same line: if the context dies *during* the
call Playwright raises "Unable to retrieve content because the page is
navigating and changing the content"; if it is already gone *at* the call, it
stalls instead. `page.close()` is unbounded for the same reason, which matters
because an outer deadline cancels into the cleanup path.
Fix, three bounds, outermost last:
* `browser_adapter.bounded_evaluate()` + a `timeout` kwarg on all three
adapters — 30s default, tighter (10s) for the optional cosmetic DOM steps
(image dimensions, consent/overlay removal) that already degrade gracefully,
generous (300s) for the virtual-scroll loop that is legitimately slow. It
raises Playwright's own `TimeoutError`, so existing `except Error` /
`except Exception` handlers keep working unchanged.
* `AsyncPlaywrightCrawlerStrategy._capture_html()` — bounded `page.content()`
with settle-and-retry: on the navigation error, wait for the new document to
reach domcontentloaded and capture again, which is the documented remedy and
far cheaper than re-running the crawl. Retries share a group budget (25s) so
a recoverable race stays nearly free while a wedged page pays once, and it
bails out early if the page cannot even reach domcontentloaded.
* `CrawlerRunConfig.total_timeout` (ms, default None) — one budget shared by
every attempt and proxy in `arun()`'s fetch loop. Complements #1923, which
adds a dispatcher-level bound: that covers `arun_many` only, so a direct
`arun()` call (e.g. the Docker /crawl path) gets no protection from it.
Measured against a local fixture origin that reproduces the race
(`max_retries: 1`, `delay_before_return_html: 2.0`, `page_timeout: 80s`):
a request that previously hung until an external 180s deadline now returns 200
with full content in 5.0s, and a permanently wedged page fails in bounded time
with the exact reason in the log instead of consuming the whole budget silently.
Tests: tests/async/test_render_call_bounds.py — 14 tests, no browser, no network.1 parent 2d8f673 commit c395b87
7 files changed
Lines changed: 524 additions & 29 deletions
File tree
- crawl4ai
- docs/md_v2
- advanced
- api
- tests/async
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1436 | 1436 | | |
1437 | 1437 | | |
1438 | 1438 | | |
| 1439 | + | |
| 1440 | + | |
| 1441 | + | |
| 1442 | + | |
| 1443 | + | |
| 1444 | + | |
| 1445 | + | |
| 1446 | + | |
1439 | 1447 | | |
1440 | 1448 | | |
1441 | 1449 | | |
| |||
1627 | 1635 | | |
1628 | 1636 | | |
1629 | 1637 | | |
| 1638 | + | |
1630 | 1639 | | |
1631 | 1640 | | |
1632 | 1641 | | |
| |||
1756 | 1765 | | |
1757 | 1766 | | |
1758 | 1767 | | |
| 1768 | + | |
1759 | 1769 | | |
1760 | 1770 | | |
1761 | 1771 | | |
| |||
2126 | 2136 | | |
2127 | 2137 | | |
2128 | 2138 | | |
| 2139 | + | |
2129 | 2140 | | |
2130 | 2141 | | |
2131 | 2142 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
26 | 31 | | |
27 | 32 | | |
28 | 33 | | |
| |||
33 | 38 | | |
34 | 39 | | |
35 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
36 | 57 | | |
37 | 58 | | |
38 | 59 | | |
| |||
333 | 354 | | |
334 | 355 | | |
335 | 356 | | |
336 | | - | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
337 | 363 | | |
338 | 364 | | |
339 | 365 | | |
| |||
511 | 537 | | |
512 | 538 | | |
513 | 539 | | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
514 | 600 | | |
515 | 601 | | |
516 | 602 | | |
| |||
1029 | 1115 | | |
1030 | 1116 | | |
1031 | 1117 | | |
1032 | | - | |
| 1118 | + | |
| 1119 | + | |
| 1120 | + | |
| 1121 | + | |
1033 | 1122 | | |
1034 | 1123 | | |
1035 | 1124 | | |
| |||
1061 | 1150 | | |
1062 | 1151 | | |
1063 | 1152 | | |
1064 | | - | |
| 1153 | + | |
1065 | 1154 | | |
1066 | 1155 | | |
1067 | 1156 | | |
| |||
1082 | 1171 | | |
1083 | 1172 | | |
1084 | 1173 | | |
1085 | | - | |
| 1174 | + | |
1086 | 1175 | | |
1087 | 1176 | | |
1088 | 1177 | | |
| |||
1126 | 1215 | | |
1127 | 1216 | | |
1128 | 1217 | | |
1129 | | - | |
| 1218 | + | |
1130 | 1219 | | |
1131 | 1220 | | |
1132 | 1221 | | |
| |||
1196 | 1285 | | |
1197 | 1286 | | |
1198 | 1287 | | |
1199 | | - | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
1200 | 1293 | | |
1201 | 1294 | | |
1202 | 1295 | | |
| |||
1428 | 1521 | | |
1429 | 1522 | | |
1430 | 1523 | | |
1431 | | - | |
1432 | | - | |
| 1524 | + | |
| 1525 | + | |
| 1526 | + | |
| 1527 | + | |
| 1528 | + | |
| 1529 | + | |
| 1530 | + | |
1433 | 1531 | | |
1434 | 1532 | | |
1435 | 1533 | | |
| |||
1532 | 1630 | | |
1533 | 1631 | | |
1534 | 1632 | | |
1535 | | - | |
| 1633 | + | |
| 1634 | + | |
1536 | 1635 | | |
1537 | 1636 | | |
1538 | 1637 | | |
| |||
1576 | 1675 | | |
1577 | 1676 | | |
1578 | 1677 | | |
1579 | | - | |
| 1678 | + | |
| 1679 | + | |
1580 | 1680 | | |
1581 | 1681 | | |
1582 | 1682 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
403 | 403 | | |
404 | 404 | | |
405 | 405 | | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
406 | 418 | | |
407 | 419 | | |
408 | 420 | | |
| |||
456 | 468 | | |
457 | 469 | | |
458 | 470 | | |
459 | | - | |
460 | | - | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
461 | 490 | | |
462 | 491 | | |
463 | 492 | | |
| |||
0 commit comments