Skip to content

[Bug]: preview_click still gives each step a full timeout after #17693, so a stalled click's reason never reaches the agent #25557

Open

Description

@pnupu

Before submitting

Area: apps/server

Steps to reproduce

Not yet run; this follows from the code paths below.

  1. Load a page where the target button is inserted after about 2 s and is disabled:
    <script>setTimeout(() => document.body.insertAdjacentHTML("beforeend", '<button id="save" disabled>Save</button>'), 2000)</script>
  2. Call preview_click immediately after loading, while #save is absent and more than one second remains before insertion (otherwise reload before retrying; this repro is timing-sensitive). Call it with selector: "#save" and the default timeoutMs (15000).

Expected behavior

Within the request's timeoutMs, the click fails with Playwright's reason (here, "element is not enabled"), and the agent can see that reason.

Actual behavior

ServerBrowserPage.click hands the full timeoutMs to each step separately rather than sharing one deadline:

const timeout = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;          // 15_000
const point = await targetPoint(page, locator, input, timeout);  // scrollIntoViewIfNeeded(timeout) + boundingBox(timeout)
await pointer(point, "click");
locator.click({ ...options, timeout })                           // a fresh 15 s

The broker starts its response timer right after enqueueing the request (timeoutMs + HOST_RESPONSE_GRACE_MS = 16 s), without waiting for the page operation to start. Nothing gives the page side the time that remains. SessionControl checks the request deadline only before queued work starts. So when queueing, waiting for the target, and preparing the click take longer than the 1 s grace, Playwright's click timeout fires after the broker has already expired. The broker then evicts the host and returns a generic Preview automation click timed out after 15000ms. with no cause. Broker expiry does not cancel a click that has already started, so it can still apply later.

Even when Playwright's timeout does arrive in time, the reason is lost twice:

  • toOperationError keeps only message.split("\n")[0], which drops Playwright's call log, where the actionability reason lives.
  • PreviewAutomationTimeoutError.message prints only the operation and duration, not the host's cause.

Observed behavior: an agent clicking the Namecheap Advanced DNS "Save" link (table.advanced-dns a.save) got this generic timeout twice. Both server spans ended at about 16,002 ms with no cause, and the target resolved about 2 s into each request. The server trace files have rotated since, so that span data is no longer available. The desktop PreviewManager.emitAgentPointer spans (16:44:30 and 16:45:16 local time) are still present.

Relationship to existing reports

Suggested fix

Establish one request deadline, calculate the remaining budget after queueing and before each step (targetPoint, locator.click), and reserve time for diagnostics and response delivery. Carry the relevant actionability line from the Playwright call log into the caller-facing timeout message.

Impact

Major degradation or frequent failure

Version or commit

T3 Code (Beta) 0.1.2. The same click timeout behavior is present in v0.1.3-nightly.20261026.3717 and origin/main.

Environment

macOS 27.0, desktop app. The shared-browser tab was driven by an agent through the T3 MCP preview tools: in-process server-browser host, desktop CDP bridge.

Workaround

On that site, filling the fields and calling element.click() from preview_evaluate worked. Before retrying, check whether a timed-out click already applied, so the action isn't duplicated.

Activity

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