Repository navigation
Description
Environment
- OS: Windows 11 Pro 10.0.22621
- opencode: 1.19.3
- Command:
opencode serve --hostname 0.0.0.0 --port 4096
Symptom
After opencode serve is terminated abnormally (Ctrl-C in its console window, or killing the parent process), the port stays bound by an orphaned LISTENING socket. Nothing accepts connections (GET /global/health → connection refused), yet a new opencode serve --port 4096 fails with ServeError:
$ node -e "require('net').createServer().listen(4096,'0.0.0.0')"
Error: listen EADDRINUSE: address already in use 0.0.0.0:4096
Only a reboot clears it. Reproduced twice on this machine: first on port 4097 (Oct 19-20), then on port 4096 (Oct 21, socket created at 15:22:17, owning PID 4936).
Evidence
> netstat -abno | findstr :4096
TCP 0.0.0.0:4096 0.0.0.0:0 LISTENING 4936
TCP 127.0.0.1:4096 127.0.0.1:50971 CLOSE_WAIT 4936 <- owner reported as [System]
TCP 127.0.0.1:4096 127.0.0.1:53892 CLOSE_WAIT 4936 <- owner reported as [System]
tasklist /FI "PID eq 4936"→ No tasks are running which match the specified criteriaGet-CimInstance Win32_Process -Filter "ProcessId=4936"→ emptyGet-NetTCPConnection -LocalPort 4096 -State Listenstill reportsOwningProcess = 4936(dead PID),CreationTime = <time of the crashed run>netsh interface portproxy show all→ empty (not a portproxy rule)wsl --shutdowndoes not clear it- Not caused by a firewall rule or an excluded port range: the socket is visible in the TCP table as LISTENING
Hypothesis
On Windows, socket handles are inheritable across CreateProcess by default (Node/Bun spawn processes with bInheritHandles = TRUE). If opencode serve creates its listening socket before spawning children (plugin/shim/worker processes), every child receives a duplicate of the listening-socket handle.
Sequence that leads to the leak:
opencode servebinds0.0.0.0:4096(owner PID 4936) and spawns children.- User presses Ctrl-C / kills only the parent. The parent dies; a surviving child still holds the duplicated handle, so the socket stays bound.
- The child later dies abnormally as well; kernel cleanup is deferred and the socket ends up referenced from the System context → a permanent
LISTENINGentry whose owning PID no longer exists.
Minimal reproduction
- Start
opencode serve --port 4096in a console. - Ctrl-C it (or
taskkill /PID <pid>on the parent only). netstat -ano | findstr :4096→ stillLISTENINGwith the dead PID.- Try to start
opencode serve --port 4096again →ServeError/ EADDRINUSE. - Reboot to recover.
Suggested fixes
- Create the listening socket as non-inheritable:
WSA_FLAG_NO_HANDLE_INHERITinWSASocket, orSetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)right after bind/socket creation. - Kill the whole process tree on termination: put spawned children in a Job Object with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so no child can outlive the server holding the handle. - Startup diagnostics: if the port is bound but
/global/healthis unreachable, print actionable guidance instead of a bareServeError(e.g. "port 4096 is held by a dead process; reboot required").
Impact
A single Ctrl-C makes the port unusable machine-wide until reboot. This is especially painful when opencode serve is managed by a service wrapper or a sidecar that restarts it automatically: the restart can never succeed, and the supervisor keeps failing silently.
Activity
opencode-agent commentedon Oct 21, 2026
Thanks for the detailed report and diagnostics, @1648298170!
You're on
1.19.3, which is OpenCode v1 (opencode-ai). v1 now only gets fixes for critical bugs such as data loss, security problems or breakage with no workaround. Starting the server on another port avoids this, so it's unlikely to be fixed in v1.We'd encourage you to try v2 (
@opencode/cli, upgrade withopencode upgrade --major). It's a completely separate implementation. By default it runs the server as a background service, which you can stop withopencode service stop. That command asks the server to exit and force-stops it if it hasn't exited after about 5 seconds.Related issues, most closely related first:
serveon Windows keeps its port after exit, so the next start fails withServeError(closed as not planned)serveon the same port fails withServeError(closed for v1)If you think this is critical, or you see the same orphaned socket on v2, please reply here with your
opencode --versionand thenetstat -ano/Get-NetTCPConnectionoutput taken right after the failure.opencode-agent commentedon Oct 22, 2026
We haven't heard back, so I'm closing this as not planned. The report is about OpenCode v1 (
1.19.3), which only gets critical fixes, and startingserveon another port avoids the stuck socket. Thanks again for the detailed diagnostics, @1648298170.If you see the same orphaned LISTENING socket on v2 (
@opencode/cli), please open a new issue, or add to #53782, with youropencode --versionand thenetstat -ano/Get-NetTCPConnectionoutput taken right after the failure.