Repository navigation
[Bug]: preview_click still gives each step a full timeout after #17693, so a stalled click's reason never reaches the agent #25557
Description
Before submitting
- I searched existing issues. Closest is [Bug]: preview_wait_for still evicts a live host when its miss reply lands at the broker deadline #12898 (closed by fix(server): keep preview browser connected after operation timeouts #17693); this is the residual it left.
- I included enough detail to reproduce or investigate the problem.
Area: apps/server
Steps to reproduce
Not yet run; this follows from the code paths below.
- 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>
- Call
preview_clickimmediately after loading, while#saveis absent and more than one second remains before insertion (otherwise reload before retrying; this repro is timing-sensitive). Call it withselector: "#save"and the defaulttimeoutMs(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 sThe 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:
toOperationErrorkeeps onlymessage.split("\n")[0], which drops Playwright's call log, where the actionability reason lives.PreviewAutomationTimeoutError.messageprints 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
- [Bug]: preview_wait_for still evicts a live host when its miss reply lands at the broker deadline #12898 already reported this per-step click budget (in this comment). Its closing fix, fix(server): keep preview browser connected after operation timeouts #17693, added 1 s of response grace but left the per-step budgets unchanged. This report covers what remains: no shared remaining-time budget, and the lost actionability reason.
- PR fix(preview): Return Request Timeouts Without Disconnecting Other Agents #23564 bounds the whole server handler by
request.timeoutMs. That prevents this stalled handler from reaching broker expiry when delivery and response overhead fit within the grace period. With that change, a stalled click returns the handler's timeout instead, so Playwright's reason is still never seen. - PR fix(providers): keep preview timeout recovery inside T3 #23107 makes timeout messages distinguish browser-side timeouts from broker timeouts. It does not keep Playwright's call log or give the click its remaining time.
- [Bug]: preview_click reports generic execution errors for missing targets #3714 / PR fix(server): preview click timeouts identify the target #23489 add a post-timeout target-count diagnostic and expose the bounded server diagnostic in the public timeout message. They don't preserve Playwright's actionability log or share one remaining-time budget, and replies arriving after broker expiry are still discarded.
- [Bug]: Windows snapshot/evaluate timeouts evict the shared server-browser host and abort another open #16921 / PR fix(preview): keep browser sessions available after action timeouts #17871 cover host eviction after timeouts.
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