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
{{ message }}
Repository navigation
[Bug] browser_wait_for silently caps time at 30 s but reports the requested duration #1767
browser_wait_for with a time argument silently caps the wait at 30 seconds, but both the reported code and the result text claim the full requested duration. A caller asking for time: 70 waits 30 s and is told Waited for 70.
Where
src/tools/wait.ts (seen in the published @playwright/mcp 0.0.80 and 0.0.82 bundles, coreBundle.js):
if(params.time){response.addCode(`await new Promise(f => setTimeout(f, ${params.time} * 1000));`);awaitnewPromise(f=>setTimeout(f,Math.min(30000,params.time*1000)));}
...
response.addTextResult(`Waited for ${params.text||params.textGone||params.time}`);
The Math.min(30000, …) clamp is fine as a safety limit, but the code line and the result text both report params.time, so the model is told something that did not happen.
Steps to reproduce
Run @playwright/[email protected] over stdio (--headless --browser chromium).
browser_navigate to any page.
Call browser_wait_for with { "time": 70 } and time the call.
Expected
Either the call waits 70 s, or the result says what actually happened (e.g. Waited for 30 (capped; requested 70)), and the tool's time description states the cap so the model does not plan around a longer wait.
Actual
The call returns after ~30 s with:
### Result
Waited for 70
### Ran Playwright code
await new Promise(f => setTimeout(f, 70 * 1000));
Observed with an LLM agent on 0.0.80 first (wall clock ~34 s for a requested 70 s, per the agent's own timestamps), then confirmed by reading the bundled source of 0.0.80 and 0.0.82.
Suggested fix
Report the clamped value in both the code line and the result, and mention the 30 s maximum in the time parameter description. Happy to open a PR if that shape is acceptable.
Good reproduction. I would make the requested duration and the effective wait separate first-class fields rather than letting the generated code and result text reuse the request. For example: requestedSeconds: 70, appliedSeconds: 30, capped: true, elapsedMs, and a documented maxSeconds: 30; the generated Playwright code should use appliedSeconds so the trace is executable as reported.
If a caller needs more than the cap, the agent can perform bounded waits with an attempt/run identity and re-observe between them. A navigation, disconnect, or target-generation change during that sequence should return an explicit interrupted/stale/unknown outcome rather than implying that the full requested wait completed. Boundary tests should cover 0, 30, just-over-30, larger values, cancellation, navigation during the wait, and a repeated bounded-wait loop.
I maintain Hronaut, a local visible Browser/MCP workspace, and this is AI-assisted technical feedback—not a Playwright MCP compatibility or integration claim.
I'd like to work on this, if it's open for a community contribution. What I have in mind, keeping the 30-second cap:
The generated code uses the time actually waited: setTimeout(f, 30 * 1000) when 70 is requested.
The result says what happened: Waited for 30 seconds (requested 70, maximum is 30). Requests under the cap are reported as they are now.
The time parameter description states the 30-second maximum, so the model can plan around it.
A test in tests/mcp/wait.spec.ts for a request over the cap. To avoid a 30-second test, I'd move the cap into a named constant and check the reported code and text; happy to test it another way if you prefer.
The code is now in packages/playwright-core/src/tools/backend/wait.ts in microsoft/playwright, so the PR would go there, linked to this issue.
Activity
Hronom commentedon Sep 22, 2026
Good reproduction. I would make the requested duration and the effective wait separate first-class fields rather than letting the generated code and result text reuse the request. For example:
requestedSeconds: 70,appliedSeconds: 30,capped: true,elapsedMs, and a documentedmaxSeconds: 30; the generated Playwright code should useappliedSecondsso the trace is executable as reported.If a caller needs more than the cap, the agent can perform bounded waits with an attempt/run identity and re-observe between them. A navigation, disconnect, or target-generation change during that sequence should return an explicit interrupted/stale/unknown outcome rather than implying that the full requested wait completed. Boundary tests should cover 0, 30, just-over-30, larger values, cancellation, navigation during the wait, and a repeated bounded-wait loop.
I maintain Hronaut, a local visible Browser/MCP workspace, and this is AI-assisted technical feedback—not a Playwright MCP compatibility or integration claim.
tsurutanmen commentedon Sep 23, 2026
I'd like to work on this, if it's open for a community contribution. What I have in mind, keeping the 30-second cap:
setTimeout(f, 30 * 1000)when 70 is requested.Waited for 30 seconds (requested 70, maximum is 30). Requests under the cap are reported as they are now.timeparameter description states the 30-second maximum, so the model can plan around it.tests/mcp/wait.spec.tsfor a request over the cap. To avoid a 30-second test, I'd move the cap into a named constant and check the reported code and text; happy to test it another way if you prefer.The code is now in
packages/playwright-core/src/tools/backend/wait.tsin microsoft/playwright, so the PR would go there, linked to this issue.chore: mark v0.0.83 (#1779)
chore: mark v0.0.83 (microsoft#1779)