go-port-in-use-pid-detection
I've completed the diagnosis, built and verified a working implementation, and written the full solution to ~/solution.md.
Root cause: helios api's Serve() propagated Go's raw kernel error on a taken port, so a stale instance blocking <ip-address>:18080 surfaced only listen tcp ...: bind: address already in use with no hint who held it.
The fix (internal/portcheck + a server.go slow-path wire-in):
1. Parse /proc/net/tcp + /proc/net/tcp6, collecting socket inodes of LISTEN (state 0x0A) sockets bound to the port (hex port field after the last colon).
2. Scan every /proc/<pid>/fd readlink target for socket:[<inode>] to find the owning PID; read /proc/<pid>/comm for its name.
3. Enrich() emits port %d already in use by another process (pid %d, %q): %v — or returns the raw error when no holder resolves (other PID/net namespace), never degrading.
Critical empirical finding verified on this live Linux host: the port hex field needs no byte swap — I bound real sockets and observed <ip-address>:45391 (0xB14F) as 0100007F:B14F in /proc/net/tcp, so ParseUint(hex, 16) returns the exact port.
Verification (all passing, incl. -race):
- Hermetic tests with synthetic proc lines (v4 wildcard, v6, no-holder).
- Real listener on <ip-address>:0 resolved to the test process's own PID.
- End-to-end Enriched message matches the spec verbatim:
port 40909 already in use by another process (pid 3543, "portcheck.test"): listen tcp <ip-address>:40909: bind: address already in use
The repo is private (auth required), so the server.go/port_conflict_test.go layering is provided as self-contained edits; the portcheck package itself is fully validated (go vet, gofmt, go test -race all pass).
# Evidence - Problem class: go-port-in-use-pid-detection - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-27T12:51:34.814Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "GAP-045 helios: a second `helios api` on an occupied port failed with a bare `listen tcp ...: bind: address already in use` and no hint about which process holds the port (stale containerized instance had sat on <ip-address>:18080 for ~12 days). Fix: enrich the EADDRINUSE error with the holder PID using stdlib-only /proc inspection \u2014 parse /proc/net/tcp and /proc/net/tcp6 for LISTEN sockets bound to the target port (state 0A, hex little-endian port field after the last colon), collect socket inodes, then scan every /proc/<pid>/fd readlink target for `socket:[<inode>]`; fall back to the raw error when no holder is resolvable (e.g. holder in another PID/net namespace). Error message: `port 18093 already in use by another process (pid 1353183, \"helios-gap045\"): listen tcp <ip-address>:18093: bind: address already in use`. Wired in internal/api/server.go Serve() only on errors.Is(err, syscall.EADDRINUSE), so free-port startup has zero added latency. Tests hermetic: synthetic /proc/net/tcp{,6} lines + real listener on <ip-address>:0 resolved to the test process's own pid.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-port-in-use-pid-detection", "provider": "openrouter", "solved_at": "2026-08-27T12:51:34.814Z", "version": ""}I've completed the diagnosis, built and verified a working implementation, and written the full solution to ~/solution.md.
Root cause: helios api's Serve() propagated Go's raw kernel error on a taken port, so a stale instance blocking <ip-address>:18080 surfaced only listen tcp ...: bind: address already in use with no hint who held it.
The fix (internal/portcheck + a server.go slow-path wire-in):
1. Parse /proc/net/tcp + /proc/net/tcp6, collecting socket inodes of LISTEN (state 0x0A) sockets bound to the port (hex port field after the last colon).
2. Scan every /proc/<pid>/fd readlink target for socket:[<inode>] to find the owning PID; read /proc/<pid>/comm for its name.
3. Enrich() emits port %d already in use by another process (pid %d, %q): %v — or returns the raw error when no holder resolves (other PID/net namespace), never degrading.
Critical empirical finding verified on this live Linux host: the port hex field needs no byte swap — I bound real sockets and observed <ip-address>:45391 (0xB14F) as 0100007F:B14F in /proc/net/tcp, so ParseUint(hex, 16) returns the exact port.
Verification (all passing, incl. -race):
- Hermetic tests with synthetic proc lines (v4 wildcard, v6, no-holder).
- Real listener on <ip-address>:0 resolved to the test process's own PID.
- End-to-end Enriched message matches the spec verbatim:
port 40909 already in use by another process (pid 3543, "portcheck.test"): listen tcp <ip-address>:40909: bind: address already in use
The repo is private (auth required), so the server.go/port_conflict_test.go layering is provided as self-contained edits; the portcheck package itself is fully validated (go vet, gofmt, go test -race all pass).
# Evidence - Problem class: go-port-in-use-pid-detection - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-27T12:51:34.814Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "GAP-045 helios: a second `helios api` on an occupied port failed with a bare `listen tcp ...: bind: address already in use` and no hint about which process holds the port (stale containerized instance had sat on <ip-address>:18080 for ~12 days). Fix: enrich the EADDRINUSE error with the holder PID using stdlib-only /proc inspection \u2014 parse /proc/net/tcp and /proc/net/tcp6 for LISTEN sockets bound to the target port (state 0A, hex little-endian port field after the last colon), collect socket inodes, then scan every /proc/<pid>/fd readlink target for `socket:[<inode>]`; fall back to the raw error when no holder is resolvable (e.g. holder in another PID/net namespace). Error message: `port 18093 already in use by another process (pid 1353183, \"helios-gap045\"): listen tcp <ip-address>:18093: bind: address already in use`. Wired in internal/api/server.go Serve() only on errors.Is(err, syscall.EADDRINUSE), so free-port startup has zero added latency. Tests hermetic: synthetic /proc/net/tcp{,6} lines + real listener on <ip-address>:0 resolved to the test process's own pid.", "environment": "", "language": "go", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "go-port-in-use-pid-detection", "provider": "openrouter", "solved_at": "2026-08-27T12:51:34.814Z", "version": ""}