Repository navigation
Description
Summary
On Windows, the V2 TUI repeatedly fails to establish the event stream (GET /api/event, SSE) at startup. Each attempt is aborted by the client after ~2.0 s with Timed out connecting to server, then retried. The TUI shows "Restarting service... Your session will resume automatically." (or "Connection lost... Reconnecting to the server automatically.") for a long time — observed up to 80 attempts (~4 min) — even though the session keeps working behind the overlay. It reproduces with the shared background service AND with a private/standalone server.
Environment
- opencode version: 2.0.29
- OS: Windows 10.0.26300.9711 (win32 x64)
- Terminal: Windows Terminal (tab running Git Bash); also reproduced launching from cmd/PowerShell
- Shell: Git Bash (
SHELL=/usr/bin/bash);ComSpec=C:\WINDOWS\system32\cmd.exe - Install/channel: npm global install,
@opencode/[email protected]under%APPDATA%\npm; channellatest - Active plugins: none found in user or project config (
~/.config/opencodehas onlycli.json+service.json; project has noopencode.json/.opencode). Logs still reportplugins=12during reconciliation.
Reproduction
- On Windows, install opencode 2.0.29 via npm global (
npm i -g @opencode/[email protected]). - Open
opencodein a terminal (Git Bash, cmd, or PowerShell). - Observe the "Restarting service..." overlay and repeated reconnects.
- Inspect
~/.local/share/opencode/log/opencode.log.
Expected Behavior
The event stream should connect promptly (first server.connected frame within the client's first-frame timeout) and the TUI should become interactive without a long reconnect overlay.
Actual Behavior
Every GET /api/event is aborted by the client after ~2.0 s. The server only logs the response when the client disconnects (http.span≈2000). The client retries many times before connecting.
Log excerpt (timestamps UTC):
... event stream connecting component=client attempt=0 role=cli
... event stream disconnected component=client attempt=1 error="Timed out connecting to server" role=cli
... event stream connecting component=client attempt=1 role=cli
... event stream disconnected component=client attempt=2 error="Timed out connecting to server" role=cli
...
... event stream connecting component=client attempt=59 role=cli
... event stream disconnected component=client attempt=60 error="Timed out connecting to server" role=cli
... event stream connecting component=client attempt=60 role=cli
... event stream connected component=client role=cli
Each attempt lasts ~2000 ms, for tens of attempts in a row.
Additional Context
- Reproduces with the shared background service (
serve --service) AND with a private server (serve --stdio --port 0), so this is not specific to the managed service. - The ~2 s first-frame timeout in the TUI SDK (
packages/tui/src/context/sdk.tsx) is also referenced by 2.0: managed-service restart causes reconnect herd and resource spikes #36285 and cli: managed reconnect waits until fourth failure to ensure service #36581. - Workarounds tried without success: reinstalling, installing an older version, and disabling the shared service (
service set disabled true). Disabling the service made no difference because the private server shows the same timeout. - After the shared service is stopped (
opencode service stop) while a TUI is connected, the client enters a reconnect storm ("Connection lost / Reconnecting..."). - Server is the Bun-based V2 server on Windows; it subscribes watchers to drive roots (
C:\,E:\).
Activity
opencode-agent commentedon Oct 15, 2026
Thanks for the detailed report and logs. Several things can cause the same 2 s
/api/eventtimeout loop on Windows, and they look the same in the log. To work out which one you're hitting, could you share:env | grep -i proxy(Git Bash) orset | findstr /i proxy(cmd). IfHTTP_PROXY/HTTPS_PROXYare set, does it still happen withNO_PROXY=127.0.0.1,localhost?curl -N http://127.0.0.1:<port>/api/event(add the same auth header or password the CLI uses, if any). Does theserver.connectedevent show up immediately, or only after a few seconds?opencodefrom. The log shows watchers onC:\andE:\, so are you launching it from a drive root? Does it also happen when you start it inside a normal project folder?You can reply here or edit the issue. If you edit it, please also leave a comment saying you've updated it, so we know when you're done.
Here are possibly related issues, ordered from most to least likely. Please check whether one of them already covers your problem:
NO_PROXYsends local traffic through the proxy, so/api/eventnever connects and the TUI shows "Restarting service…". Another Windows CLI user confirmed this in the comments./api/eventis delayed past the 2 s deadline because the client doesn't sendAccept: text/event-stream.OPENCODE_DISABLE_FILEWATCHER=1avoids it.AcSsalazar commentedon Oct 15, 2026
Root cause narrowed down — this is server-side, and reproducible with an external HTTP client (not the TUI).
I measured
/api/eventdirectly withcurlagainst the running server (auth: HTTP Basic,opencode:<password-from-service.json>):GET /api/info→ first byte in ~8 ms.GET /api/event→ first byte at exactly ~15.003 s, reproducible (15.003816 s / 15.003771 s). First bytes received:So the SSE response does not flush its headers or the initial
server.connectedframe immediately; the first flush happens at the heartbeat tick. The TUI aborts the stream at ~2 s (Timed out connecting to server), so it only ever connects when a bus event happens to be published inside that 2 s window — which is exactly the reconnect loop / "Restarting service..." overlay reported here.This matches
packages/server/src/handlers/event.ts(V2):The initial
server.connectedframe is produced only after the live feed is acquired (EventV2.allBounded), and the streaming response does not send headers until its first element is pulled — which here is the 15 s heartbeat.Environment
latestProxyEnable=0, no*_PROXYenv vars)/api/info,/api/provider,/api/model,/api/config, ...) respond in <30 msSuggested fix
Flush the SSE response headers immediately and emit
server.connectedbefore/independently of acquiring the live feed, instead of relying on the 15 s heartbeat (or the next bus event) for the first flush. Alternatively/additionally, raise the TUI's ~2 s first-frame timeout so it tolerates a slow first flush.opencode-agent commentedon Oct 15, 2026
Thanks for the
curlmeasurements, they help a lot.I tried this on Linux with 2.0.29 (
opencode serve, started both in a project and from/) and couldn't reproduce it./api/eventsent its headers, theserver.connectedframe and a first: heartbeatwithin 1–4 ms, about as fast as/api/info. The 15 s heartbeat timer also fires once as soon as the stream opens. In your capture,server.connectedarrived together with two heartbeats at ~15 s. So the server seems to have written the first frame right away, and something on your machine held those bytes until the next heartbeat about 15 s later. That points to something specific to Windows rather than the event handler waiting before it starts. It could be how the Bun-based server streams on Windows, or software that inspects local HTTP traffic.This needs a maintainer with a Windows machine to dig further. In the meantime, these would help narrow it down:
/api/event.-H 'Accept: text/event-stream'to yourcurlmake the first byte arrive immediately? That header was what made the difference in V2 event streams can stall with Kaspersky without an SSE Accept header #48771.curlwith Defender's real-time protection briefly turned off, or with127.0.0.1excluded.AcSsalazar commentedon Oct 15, 2026
Also reproduced on the dev channel build
@opencode/[email protected]: isolatedopencode serveon Windows,GET /api/eventfirst byte at 15.008 s (same as 2.0.28/2.0.29). So this is not fixed in current builds, stable or dev.Additional evidence: if any bus event is published shortly after subscribing, the stream flushes immediately. Subscribing then calling
POST /api/location/reload~1.5 s later yields the first byte at 1.53 s (server.connected+ a burst of*.updatedevents) instead of 15 s. This confirms the initial frame/headers are simply not flushed until a second chunk arrives (heartbeat or any bus event).moaberdan commentedon Oct 15, 2026
Reproduced on my machine as well.
Environment
2.0.29@opencode/[email protected], channellatest)HTTP_PROXY/HTTPS_PROXYenv varsObserved behavior
In normal mode (
opencodefrom project directory):In standalone mode (
opencode --standalone):Logs
Same
Timed out connecting to serverloop on/api/event:Server logs show
/api/eventreturning HTTP 200, but the client keeps timing out.What I already tried
opencode service restart)~/.local/state/opencode/latest/tui/tabs.jsonThe popup delay is shorter after cleaning the TUI state, but the underlying SSE first-frame timeout on Windows is still present — exactly as described in this issue.
AcSsalazar commentedon Oct 15, 2026
Confirmed: this is Kaspersky, independent of OpenCode.
Isolated test with a minimal Node HTTP SSE server (no OpenCode involved), listening on 127.0.0.1 and writing one SSE frame immediately:
GET /ssewithoutAccept: text/event-stream→ curl receives no response headers at all within 8 s (http_code=000).GET /ssewithAccept: text/event-stream→ first byte in ~0.003 s (http_code=200).So Kaspersky Endpoint Security for Windows (KES 14.1.0.423) buffers loopback
text/event-streamresponses whenever the request lacksAccept: text/event-stream— for any server, not just OpenCode. This is exactly #48771.The OpenCode TUI simply doesn't send that header on
/api/event, so on machines with Kaspersky the first frame is held past the ~2 s deadline and the client loops.Workarounds: exclude
localhost/127.0.0.1(oropencode.exe) from Kaspersky's local traffic inspection, or have the client sendAccept: text/event-streamon the event subscription.