#17946: fix(browser): prevent act timeout on Linux by removing premature abort trigger
stale
size: XS
## Summary
Fixes browser `act` timeout on Linux for click/type/fill operations by removing premature abort trigger in Express middleware.
Resolves #11691
## Problem
Browser `act` operations (click, type, fill) timeout on Linux while working correctly on macOS. Other browser actions (navigate, snapshot, screenshot) work fine on both platforms.
**Symptoms:**
- Click/type/fill timeout after 30 seconds on Linux
- Playwright logs show: "element is visible, enabled and stable" → "retrying click action" → timeout
- Same operations work on macOS without issues
## Root Cause
Commit `424d2ddf` (v2026.2.12) added AbortSignal handling to prevent stuck evaluate operations. The middleware in `src/browser/server-middleware.ts` creates an AbortController for every HTTP request and aborts on `res.close`:
```typescript
res.once("close", () => {
if (!res.writableEnded) {
abort();
}
});
```
**The bug:** On Linux, Express fires the `res.close` event **before** `writableEnded` is set during long-running Playwright operations. This causes:
1. Premature `ctrl.abort()` trigger mid-operation
2. Abort signal propagates to `forceDisconnectPlaywrightForTarget()`
3. Playwright connection forcibly disconnected during click/type/fill
4. Operation fails with timeout
macOS handles `res.close` differently and doesn't exhibit this behavior.
## Fix
Removed the `res.close` abort trigger from the middleware. The abort protection is now scoped only to the `req.aborted` event, which reliably detects actual client disconnections across platforms.
**File changed:** `src/browser/server-middleware.ts`
**Lines changed:** 7 insertions(+), 5 deletions(-)
## Testing
Tested on **Ubuntu 24.04** EC2 instance with TrackMyTrades login form:
✅ **Click** "Log In" button → completed successfully
✅ **Type** email field → completed successfully
✅ **Type** password field → completed successfully
✅ **Click** "Login" button → completed successfully
✅ Successfully logged into application
All operations completed without timeout. Browser profile: `openclaw` (Brave headless).
## Rationale
1. **Minimal change:** Removes only the problematic `res.close` handler
2. **Platform-agnostic:** Relies on `req.aborted` which works consistently on Linux and macOS
3. **Preserves abort protection:** Still catches actual client disconnections
4. **Non-breaking:** Restores pre-v2026.2.12 behavior for click/type/fill while keeping other improvements
## Checklist
- [x] Root cause identified via git bisect
- [x] Fix tested on Linux (Ubuntu 24.04)
- [x] No regression on macOS expected (relies on standard Express events)
- [x] Minimal code change (7 lines)
- [x] Related issue referenced (#11691)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes browser `act` timeouts (click/type/fill) on Linux by removing the `res.close` abort handler from the Express middleware in `src/browser/server-middleware.ts`. The `res.close` event was firing prematurely on Linux during long-running Playwright operations, causing the `AbortController` to abort mid-operation.
- **Potential issue**: The remaining `req.once("aborted", ...)` listener relies on a **deprecated** Node.js API ([DEP0133](https://nodejs.org/api/deprecations.html#DEP0133), since v16.12.0). With the project requiring `node >= 22.12.0`, this event may not fire reliably and could silently leave the abort signal inert on client disconnections. A safer cross-platform approach would be `req.once("close", () => { if (req.destroyed) abort(); })`.
- The abort signal is currently only consumed by `evaluateViaPlaywright` (via `req.signal` in `agent.act.ts:321`), not by click/type/fill operations, so the functional impact of this change is limited to evaluate abort protection.
- Commented-out code should be deleted in favor of relying on version control history.
<h3>Confidence Score: 2/5</h3>
- The fix addresses the Linux timeout but introduces a reliance on a deprecated Node.js API that may silently fail.
- Score of 2 reflects that while the PR correctly identifies and removes the problematic `res.close` handler, the remaining abort mechanism (`req.once("aborted", ...)`) uses deprecated Node.js API DEP0133. On the project's required Node.js >=22.12.0, this event may not fire, meaning client disconnection detection could silently stop working. The correct fix should use `req.once("close", ...)` with `req.destroyed` instead.
- `src/browser/server-middleware.ts` — the abort listener uses a deprecated event that should be replaced with the modern `req.close` + `req.destroyed` pattern.
<sub>Last reviewed commit: ae036de</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#9693: fix: add configurable browser.actTimeoutMs to prevent action timeouts
by JitendraZaa · 2026-02-05
76.3%
#23745: fix(resilience): add timeout to unguarded fetch calls in browser su...
by kevinWangSheng · 2026-02-22
73.1%
#18907: Fix: Improve browser error messages to avoid misleading agents
by jriff · 2026-02-17
72.1%
#11692: feat: add standalone browser CLI for Linux users
by shipitirl · 2026-02-08
72.0%
#19495: fix: handle Chrome process re-parenting on Windows
by andenwick · 2026-02-17
71.4%
#23075: fix(browser): merge top-level ref/targetId into act request body
by Remixer33 · 2026-02-22
70.3%
#3794: fix(browser-tool): disallow close without targetId to avoid unsafe ...
by JaydenLiang · 2026-01-29
70.1%
#14944: fix(browser): prefer openclaw profile in headless/noSandbox environ...
by BenediktSchackenberg · 2026-02-12
70.0%
#6686: fix: clear Playwright's default colorScheme override on CDP-connect...
by Terwox · 2026-02-01
69.5%
#23668: fix: distinguish browser validation errors from connectivity failures
by davidemanuelDEV · 2026-02-22
69.5%