Windows: orphaned LISTENING socket after opencode serve termination (EADDRINUSE until reboot) · Issue #57723 · anomalyco/opencode · GitHub
Skip to content

Windows: orphaned LISTENING socket after opencode serve termination (EADDRINUSE until reboot) #57723

Open

Description

@1648298170

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 criteria
  • Get-CimInstance Win32_Process -Filter "ProcessId=4936" → empty
  • Get-NetTCPConnection -LocalPort 4096 -State Listen still reports OwningProcess = 4936 (dead PID), CreationTime = <time of the crashed run>
  • netsh interface portproxy show all → empty (not a portproxy rule)
  • wsl --shutdown does 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:

  1. opencode serve binds 0.0.0.0:4096 (owner PID 4936) and spawns children.
  2. User presses Ctrl-C / kills only the parent. The parent dies; a surviving child still holds the duplicated handle, so the socket stays bound.
  3. The child later dies abnormally as well; kernel cleanup is deferred and the socket ends up referenced from the System context → a permanent LISTENING entry whose owning PID no longer exists.

Minimal reproduction

  1. Start opencode serve --port 4096 in a console.
  2. Ctrl-C it (or taskkill /PID <pid> on the parent only).
  3. netstat -ano | findstr :4096 → still LISTENING with the dead PID.
  4. Try to start opencode serve --port 4096 again → ServeError / EADDRINUSE.
  5. Reboot to recover.

Suggested fixes

  1. Create the listening socket as non-inheritable: WSA_FLAG_NO_HANDLE_INHERIT in WSASocket, or SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0) right after bind/socket creation.
  2. 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.
  3. Startup diagnostics: if the port is bound but /global/health is unreachable, print actionable guidance instead of a bare ServeError (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

  1. opencode-agent commented on Oct 21, 2026

    @opencode-agent
    Contributor

    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 with opencode upgrade --major). It's a completely separate implementation. By default it runs the server as a background service, which you can stop with opencode 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:

    If you think this is critical, or you see the same orphaned socket on v2, please reply here with your opencode --version and the netstat -ano / Get-NetTCPConnection output taken right after the failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions