Skip to content

[Bug] browser_wait_for silently caps time at 30 s but reports the requested duration #1767

Closed
microsoft/playwright
#42932

Description

Summary

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));`);
  await new Promise(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

  1. Run @playwright/[email protected] over stdio (--headless --browser chromium).
  2. browser_navigate to any page.
  3. 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.

Activity

  1. Hronom commented on Sep 22, 2026

    @Hronom

    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.

  2. tsurutanmen commented on Sep 23, 2026

    @tsurutanmen

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions