Pi extension: server.close() doesn't free port — next annotate/review fails with EADDRINUSE
Package: @plannotator/pi-extension
Version: 0.25.1
Runtime: Node.js (via jiti in Pi agent)
Symptom
After a /plannotator-annotate or /plannotator-review session ends, the next attempt fails:
Error: Failed to start annotation UI: Port 19432 in use after 5 retries
(set PLANNOTATOR_PORT to use a different port)
The port stays bound until the pi process is restarted. In remote mode (PLANNOTATOR_REMOTE=1) the default fixed port 19432 means every session hits this eventually.
Root cause
Three Node.js HTTP servers (annotate, plan review, code review) call server.close() in their stop() method, but close() only stops accepting new connections. If the browser held keep-alive connections (which browsers do), the OS keeps the port in LISTEN state until those connections time out — which means forever in a long-lived pi process.
The Bun-equivalent servers were fixed in #1064 for OpenCode, but the Pi extension's Node.js server code was missed.
Affected code
server/serverAnnotate.ts — stop() calls server.close() without closeAllConnections()
server/serverPlan.ts — same
server/serverReview.ts — same
Local fix applied
Two changes in node_modules/@plannotator/pi-extension/server/:
-
server.closeAllConnections() added before server.close() in all three stop() methods — destroys keep-alive connections so close() actually frees the port.
-
tryShutdownStaleServer() in network.ts — listenOnPort() now preemptively calls POST /api/exit on any server already bound to the target port before retrying, as defense-in-depth.
Steps to reproduce
- Set
PLANNOTATOR_REMOTE=1 (or SSH/devcontainer)
- Run
/plannotator-annotate somefile.md
- Close the browser tab or approve/deny
- Run
/plannotator-annotate otherfile.md
- →
Port 19432 in use after 5 retries
Upstream fix needed
Add server.closeAllConnections() before server.close() in:
apps/pi-extension/server/serverAnnotate.ts
apps/pi-extension/server/serverPlan.ts
apps/pi-extension/server/serverReview.ts
Optionally, add the preemptive /api/exit shutdown in apps/pi-extension/server/network.ts listenOnPort().
closeAllConnections() is available in Node.js 18+, which is the minimum Pi targets.
Plannotator version
0.25.1
OS
Linux
Agent
Pi
Where did it happen?
Annotate
Pi extension:
server.close()doesn't free port — next annotate/review fails with EADDRINUSEPackage:
@plannotator/pi-extensionVersion: 0.25.1
Runtime: Node.js (via jiti in Pi agent)
Symptom
After a
/plannotator-annotateor/plannotator-reviewsession ends, the next attempt fails:The port stays bound until the pi process is restarted. In remote mode (
PLANNOTATOR_REMOTE=1) the default fixed port 19432 means every session hits this eventually.Root cause
Three Node.js HTTP servers (annotate, plan review, code review) call
server.close()in theirstop()method, butclose()only stops accepting new connections. If the browser held keep-alive connections (which browsers do), the OS keeps the port in LISTEN state until those connections time out — which means forever in a long-lived pi process.The Bun-equivalent servers were fixed in #1064 for OpenCode, but the Pi extension's Node.js server code was missed.
Affected code
server/serverAnnotate.ts—stop()callsserver.close()withoutcloseAllConnections()server/serverPlan.ts— sameserver/serverReview.ts— sameLocal fix applied
Two changes in
node_modules/@plannotator/pi-extension/server/:server.closeAllConnections()added beforeserver.close()in all threestop()methods — destroys keep-alive connections soclose()actually frees the port.tryShutdownStaleServer()innetwork.ts—listenOnPort()now preemptively callsPOST /api/exiton any server already bound to the target port before retrying, as defense-in-depth.Steps to reproduce
PLANNOTATOR_REMOTE=1(or SSH/devcontainer)/plannotator-annotate somefile.md/plannotator-annotate otherfile.mdPort 19432 in use after 5 retriesUpstream fix needed
Add
server.closeAllConnections()beforeserver.close()in:apps/pi-extension/server/serverAnnotate.tsapps/pi-extension/server/serverPlan.tsapps/pi-extension/server/serverReview.tsOptionally, add the preemptive
/api/exitshutdown inapps/pi-extension/server/network.tslistenOnPort().closeAllConnections()is available in Node.js 18+, which is the minimum Pi targets.Plannotator version
0.25.1
OS
Linux
Agent
Pi
Where did it happen?
Annotate