You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
checkAndHandleRepeatedAction() decides "stuck in a loop" from the action signature alone. It doesn't consider whether the action succeeded or whether the page changed. Any UI requiring repeated operation of one control — a date picker's "next month" button, pagination, a stepper, "load more" — is therefore indistinguishable from a loop, and the task aborts on the 4th press.
This is a direct consequence of #430 working as designed, and #430 named the missing piece. See below.
#430 correctly diagnosed that the old action:ref:value signature was useless because refs churn every snapshot, so genuine loops went undetected. It proposed dropping the ref, with richer role+name matching as a follow-up. That follow-up shipped — webAgent.ts:1821 now returns action:role:name:value.
That fix did what it set out to do. But making the signature ref-independent also made legitimate repeated interaction with a single control collapse to one signature, and there's no counterweight. #430 anticipated the shape of this:
There's no detection of stagnant pages (same URL/title/major content for N steps regardless of action) — a useful signal separate from repeated actions.
That signal is now load-bearing, not just nice-to-have: it's the only thing that can distinguish "clicked the same button 4 times and nothing happened" from "clicked the same button 4 times and advanced 4 months." This issue is the measured cost of not having it.
Also still outstanding from #430: the proposed rename maxRepeatedActions → repetitionWarningCount / repetitionAbortCount didn't happen. Current source still has max_repeated_actions (default 2, warn at 3, abort at 4) with the same off-by-two confusion #430 called out.
Impact
Published hundred.jsonl runs, build 7d0f381688f5394892eead1fe28fe4fd7ebf6a9f:
5 of pilo's 6 Booking failures, all with the identical abort.
Booking pass rate 38% (9/24) against browser-use's 100% (17/17) on the same tasks in the same window (4 pilo runs on a pinned digest, 95% Wilson intervals non-overlapping). browser-use has no comparable guard.
6 of 52 failures overall; the sixth is a webSearch repetition, since webSearch isn't exempt either.
The eval dataset asks for check-in dates 2–5 months out (Sep, Nov, Dec 2026). The picker's only forward control is "next month", so reaching the target month requires 3–5 identical clicks. The 4th aborts.
Worth flagging: this had been recorded on the eval side as a site problem — "Booking's date picker is aggressively bot-hostile, headless browsers consistently fail to interact with it." That was wrong, and the traces say so: browser:action_completed carries no error and the calendar advances each time. The clicks work. Pilo stops itself. (Corrected in the eval repo.)
Mechanism
packages/core/src/webAgent.ts:1821 — signature is semantic, carries no page state:
Every "next month" click yields the same string, e.g. click:button:next month:.
Thresholds at webAgent.ts:1456-57, default from config/defaults.ts:519 (max_repeated_actions: 2) → warn at 3, abort at 4. webAgent.ts:345 exempts only Scroll and Wait. The counter increments at line 1468 purely on signature equality — before the action runs, and regardless of whether the previous attempt succeeded.
Observed trace (webvoyagerx--Booking--32)
35 ACTION click ref=E94
43 ACTION click ref=E94
51 ACTION click ref=E94
55 STATUS Warning: Repeated action detected - click:button:mes siquiente:
60 ACTION click ref=E94
64 ABORT Excessive repetition of action 'click' (4 times). The agent appears to be stuck in a loop.
Four successful month advances, read as a loop.
Stale refs compound it: a ref invalidated mid-interaction forces a correct retry of the same semantic target, producing an identical signature and consuming budget needed for legitimate paging. Booking--22 shows both interleaved.
Suggested fix
A repeat should mean same action, same resulting state — not same signature. In preference order:
Reset the counter when the action changed the page.handleBrowserDisconnect already does something analogous at webAgent.ts:2176-77 (resetting repetition state after a reconnect, precisely to avoid false loop detection), so "this repeat isn't a loop" already exists as a concept in the codebase.
Extend REPETITION_EXEMPT_ACTIONS to known paging controls. Cheapest, but label matching is locale-dependent (below) and it won't generalise.
Raising max_repeated_actions is a mitigation, not a fix — a date picker may need many more than 4 advances, and a higher threshold lets genuine loops burn more budget.
Also worth knowing (not a pilo bug)
3 of 7 sampled traces show the button labelled in Spanish (click:button:mes siquiente:), because the Bright Data exit IP geolocates outside the US on a substantial fraction of sessions. Relevant to option 3 — any label-based special-casing must be locale-agnostic.
Reproduction
Not yet run, and this one is cheap and decisive:
Run a Booking task with a check-in 3+ months out (e.g. webvoyagerx--Booking--32, Sydney, Dec 2026).
Watch for agent:status "Warning: Repeated action detected - click:button:next month:" then task:aborted.
Re-run with --max-repeated-actions 12. If it then progresses past the date picker, the guard is confirmed as the blocker rather than the site.
Step 3 is the discriminator and is worth running before picking a fix.
Regression guard: drive checkAndHandleRepeatedAction() with four identical successful click outputs where page state changes between each, and assert no abort. packages/core/test/webAgent.test.ts has repetition-abort tests at ~4470, ~4683, ~4902, ~5048 to model on.
Source claims cited against mozilla/pilo@7446077e. Evidence: 6 task artifacts from scheduled-bu-pilo-tab-hundred runs 1785337200 / 1785423600, participant pilo-cli — public, no auth: https://storage.googleapis.com/pilo-public-eval-reports/reports/<run>/<task-id>/result.json. Full analysis: Mozilla-Ocho/pilo-evals-judge#122.
Summary
checkAndHandleRepeatedAction()decides "stuck in a loop" from the action signature alone. It doesn't consider whether the action succeeded or whether the page changed. Any UI requiring repeated operation of one control — a date picker's "next month" button, pagination, a stepper, "load more" — is therefore indistinguishable from a loop, and the task aborts on the 4th press.This is a direct consequence of #430 working as designed, and #430 named the missing piece. See below.
Relationship to #430
#430 correctly diagnosed that the old
action:ref:valuesignature was useless because refs churn every snapshot, so genuine loops went undetected. It proposed dropping the ref, with richer role+name matching as a follow-up. That follow-up shipped —webAgent.ts:1821now returnsaction:role:name:value.That fix did what it set out to do. But making the signature ref-independent also made legitimate repeated interaction with a single control collapse to one signature, and there's no counterweight. #430 anticipated the shape of this:
That signal is now load-bearing, not just nice-to-have: it's the only thing that can distinguish "clicked the same button 4 times and nothing happened" from "clicked the same button 4 times and advanced 4 months." This issue is the measured cost of not having it.
Also still outstanding from #430: the proposed rename
maxRepeatedActions→repetitionWarningCount/repetitionAbortCountdidn't happen. Current source still hasmax_repeated_actions(default 2, warn at 3, abort at 4) with the same off-by-two confusion #430 called out.Impact
Published
hundred.jsonlruns, build7d0f381688f5394892eead1fe28fe4fd7ebf6a9f:webSearchrepetition, sincewebSearchisn't exempt either.The eval dataset asks for check-in dates 2–5 months out (Sep, Nov, Dec 2026). The picker's only forward control is "next month", so reaching the target month requires 3–5 identical clicks. The 4th aborts.
Worth flagging: this had been recorded on the eval side as a site problem — "Booking's date picker is aggressively bot-hostile, headless browsers consistently fail to interact with it." That was wrong, and the traces say so:
browser:action_completedcarries no error and the calendar advances each time. The clicks work. Pilo stops itself. (Corrected in the eval repo.)Mechanism
packages/core/src/webAgent.ts:1821— signature is semantic, carries no page state:Every "next month" click yields the same string, e.g.
click:button:next month:.Thresholds at
webAgent.ts:1456-57, default fromconfig/defaults.ts:519(max_repeated_actions: 2) → warn at 3, abort at 4.webAgent.ts:345exempts onlyScrollandWait. The counter increments at line 1468 purely on signature equality — before the action runs, and regardless of whether the previous attempt succeeded.Observed trace (
webvoyagerx--Booking--32)Four successful month advances, read as a loop.
Stale refs compound it: a ref invalidated mid-interaction forces a correct retry of the same semantic target, producing an identical signature and consuming budget needed for legitimate paging.
Booking--22shows both interleaved.Suggested fix
A repeat should mean same action, same resulting state — not same signature. In preference order:
handleBrowserDisconnectalready does something analogous atwebAgent.ts:2176-77(resetting repetition state after a reconnect, precisely to avoid false loop detection), so "this repeat isn't a loop" already exists as a concept in the codebase.REPETITION_EXEMPT_ACTIONSto known paging controls. Cheapest, but label matching is locale-dependent (below) and it won't generalise.Raising
max_repeated_actionsis a mitigation, not a fix — a date picker may need many more than 4 advances, and a higher threshold lets genuine loops burn more budget.Also worth knowing (not a pilo bug)
3 of 7 sampled traces show the button labelled in Spanish (
click:button:mes siquiente:), because the Bright Data exit IP geolocates outside the US on a substantial fraction of sessions. Relevant to option 3 — any label-based special-casing must be locale-agnostic.Reproduction
Not yet run, and this one is cheap and decisive:
webvoyagerx--Booking--32, Sydney, Dec 2026).agent:status"Warning: Repeated action detected - click:button:next month:" thentask:aborted.--max-repeated-actions 12. If it then progresses past the date picker, the guard is confirmed as the blocker rather than the site.Step 3 is the discriminator and is worth running before picking a fix.
Regression guard: drive
checkAndHandleRepeatedAction()with four identical successful click outputs where page state changes between each, and assert no abort.packages/core/test/webAgent.test.tshas repetition-abort tests at ~4470, ~4683, ~4902, ~5048 to model on.Source claims cited against
mozilla/pilo@7446077e. Evidence: 6 task artifacts fromscheduled-bu-pilo-tab-hundredruns 1785337200 / 1785423600, participantpilo-cli— public, no auth:https://storage.googleapis.com/pilo-public-eval-reports/reports/<run>/<task-id>/result.json. Full analysis: Mozilla-Ocho/pilo-evals-judge#122.🤖 Investigated with Claude Code