Skip to content

Pi extension: server.close() doesn't free port — next annotate/review fails with EADDRINUSE #1159

Description

@SyahrulBhudiF

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.tsstop() 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/:

  1. server.closeAllConnections() added before server.close() in all three stop() methods — destroys keep-alive connections so close() actually frees the port.

  2. tryShutdownStaleServer() in network.tslistenOnPort() now preemptively calls POST /api/exit on any server already bound to the target port before retrying, as defense-in-depth.

Steps to reproduce

  1. Set PLANNOTATOR_REMOTE=1 (or SSH/devcontainer)
  2. Run /plannotator-annotate somefile.md
  3. Close the browser tab or approve/deny
  4. Run /plannotator-annotate otherfile.md
  5. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions