neo MCP: sessions die after ~300 s idle; the SSE 404 is treated as recoverable, so a dead session is kept until the next POST (and tab ownership is lost)
Environment: BrowserOS neo 0.50.5.0 · Chromium 151.0.8162.137 · Windows 10 x64 · client = Claude Code 2.1.259 over MCP, streamable HTTP at http://127.0.0.1:9010/mcp.
What happens
After roughly 300 s with no tool call, the session is gone server-side. The client does not learn this when it happens. Its SSE GET stream drops, the reopen returns 404, and it logs
Connection error: Streamable HTTP error: Failed to open SSE stream: Not Found
Connection error: Maximum reconnection attempts (2) exceeded.
SSE GET-stream reconnection exhausted; leaving transport up (POST still works)and carries on. The next POST then fails with Not Found: Session not found, which forces a reconnect into a new session id. Since the agent identity is bound to the session id (mcp session initialized session_id=… agent=claude-code-…), every previously owned tab becomes unreachable with page N is not owned by this agent.
So a single idle period costs the agent all of its prepared page state, and it only finds out one call later.
Measurements
Idle interval before the SSE 404, measured from the last Calling MCP tool line in the client log:
| # | idle before failure | source |
|---|---|---|
| 1–5 | 305 / 317 / 308 / 309 / 315 s | occurred naturally in one sitting |
| 6 | 303 s | deliberate reproduction |
| 7 | 304 s | deliberate reproduction |
Negative control: a 199 s gap never failed, and across 59 tool calls in that session no interval ≤ 300 s produced a single failure.
One thing that cost me time and may cost others the same: the HTTP connection dropped after Ns uptime figure in the client log is uptime-since-transport-create (407–686 s across these events, no pattern) and is not the trigger. The idle interval is. The uptime number looks like the answer and isn't.
Server side
claw-server.log for one day: 63 create new session lines and zero session-close lines. The expiry is entirely silent — it is only visible as GET /mcp → 404 pairs, followed minutes later by a POST /mcp → 404. Roughly 40 such 404 events in a single day.
The process never restarted: the main browser pid was up for >20 h across four of these failures, so this is not a crash or a respawn.
Why it hurts
A five-minute pause is ordinary — composing a long reply, or waiting on a user. Anything the agent had set up on the page (injected helpers, a pre-resolved snapshot ref, an armed poller) dies with the session, silently, and the agent discovers it only when it next acts.
Ruled out: CLAW_SESSION_IDLE_MS
Set to 60000 and confirmed present in the claw-server process environment (read out of the process PEB, with PATH/USERNAME/SystemRoot as a positive control for the read itself), the timeout was still 304 s.
The family is read: CLAW_LOG=debug, delivered the same way, did turn on DEBUG sqlx::query: lines against a baseline that had none. CLAW_SESSION_{IDLE,RETENTION,SWEEP_INTERVAL}_MS look like they belong to the audit-retention sweeper instead, which runs on an exact 3600 s cadence (10 consecutive intervals, zero deviation).
A delivery note in case it helps anyone reproducing: on Windows these variables only arrive via the user/machine environment. A value injected into the shell that launches the browser is lost, because the launched chrome.exe relaunches itself and the replacement rebuilds its environment block.
I could not find any exposed knob for this timeout — the only env vars in the server binary are BROWSERCLAW_DIR, CLAW_LOG, CLAW_POSTHOG_KEY, CLAW_POSTHOG_HOST, CLAW_ANALYTICS_ENABLED and the three CLAW_SESSION_* above.
Suggested fixes
- Keep the SSE stream alive with periodic
: pingcomments, and/or - treat a 404 on SSE reopen as session death rather than logging
POST still works— that message is actively misleading, and it is the reason the failure surfaces one call late instead of immediately, and - ideally let a reconnect reclaim the tabs its predecessor owned (same end-state as #2702, different cause).
(2) alone would already turn a silent loss into an immediate, diagnosable error.
Possibly adjacent
#2696, #2699 and #2704 all touch session lifecycle, and #2699 notes the idle sweeper does fire. This report is specifically the idle path on the streamable-HTTP transport, and the client-side handling of the 404 it produces.
Happy to run further experiments on this machine if a maintainer wants a specific measurement.
Source: browseros-ai/BrowserOS