You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A wedged claude-smart backend that still holds its port (8071) in LISTEN but no longer answers /health permanently blocks restart. Every subsequent backend-service.sh start fails to bind (WinError 10048 / EADDRINUSE) and the dashboard returns 502 on /preferences (and other data routes) because its proxy target is dead. start never self-heals this, even though the holder is our own stale process.
Distinct from #107: #107 is start no-op'ing under IDE entrypoints; this is start giving up when the port is held by a dead-but-listening reflexio backend.
Reproduction / evidence
Wedged old backend (PID answering TCP, not /health):
[backend ] INFO: Application startup complete.
[backend ] ERROR: [Errno 10048] error while attempting to bind on address ('0.0.0.0', 8071): ...
[backend ] exited with code 1
backend-service.sh status reports not running (health probe fails), yet start refuses to act because port_occupied is true. Manual Stop-Process -Id 12144 + start recovers immediately.
Root cause
Three compounding gaps in plugin/scripts/backend-service.sh:
start never reaps a stale own-backend. The start) case does:
if is_our_backend_running;then emit_ok;exit 0;fiif port_occupied;then# logs "held by another process; skipping start"
emit_ok;exit 0
fi
is_our_backend_running is false (no /health), so it falls into port_occupied and gives up. reap_port_listeners only runs inside full_stop (the stop/restart path), never here. A held-but-unhealthy port that matches our own *reflexio*/*uvicorn* cmdline should be reaped and retried, not skipped.
reap_port_listeners depends on lsof (command -v lsof || return 0). On Windows git-bash lsof is absent, so reap silently no-ops — on the exact platform where this occurred. Needs a Windows fallback (netstat -ano / Get-NetTCPConnection -> taskkill /F).
Empty/stale PID file means full_stop's kill_group "$(cat "$PID_FILE")" has nothing to kill, so even an explicit restart can't clear the wedged process on Windows (reap is the only other path, and it's lsof-gated per refactor(cite): rank+fingerprint citation ids + uninstall subcommand #2).
Suggested fix
In the start) case, when port_occupied but ! backend_healthy and the holder cmdline matches our reflexio/uvicorn patterns, reap it and retry the bind (reuse reap_port_listeners), instead of emit_ok; exit 0. Keep the current "foreign holder -> skip" behavior for non-matching cmdlines.
Give reap_port_listeners / port_holder a Windows fallback when lsof is missing (netstat -ano to map port->PID, tasklist to match the command, taskkill /F /PID).
Environment
claude-smart v0.2.48
Claude Code VS Code extension, Windows 11 (git-bash)
uv / Python 3.12 present
Related: #107 (same file, _lib.sh/backend-service.sh service-startup robustness). Happy to open a PR.
Summary
A wedged claude-smart backend that still holds its port (
8071) inLISTENbut no longer answers/healthpermanently blocks restart. Every subsequentbackend-service.sh startfails to bind (WinError 10048 / EADDRINUSE) and the dashboard returns 502 on/preferences(and other data routes) because its proxy target is dead.startnever self-heals this, even though the holder is our own stale process.Distinct from #107: #107 is
startno-op'ing under IDE entrypoints; this isstartgiving up when the port is held by a dead-but-listening reflexio backend.Reproduction / evidence
Wedged old backend (PID answering TCP, not
/health):Fresh start then loses the bind race:
backend-service.sh statusreportsnot running(health probe fails), yet start refuses to act becauseport_occupiedis true. ManualStop-Process -Id 12144+ start recovers immediately.Root cause
Three compounding gaps in
plugin/scripts/backend-service.sh:startnever reaps a stale own-backend. Thestart)case does:is_our_backend_runningis false (no/health), so it falls intoport_occupiedand gives up.reap_port_listenersonly runs insidefull_stop(the stop/restart path), never here. A held-but-unhealthy port that matches our own*reflexio*/*uvicorn*cmdline should be reaped and retried, not skipped.reap_port_listenersdepends onlsof(command -v lsof || return 0). On Windows git-bashlsofis absent, so reap silently no-ops — on the exact platform where this occurred. Needs a Windows fallback (netstat -ano/Get-NetTCPConnection->taskkill /F).Empty/stale PID file means
full_stop'skill_group "$(cat "$PID_FILE")"has nothing to kill, so even an explicit restart can't clear the wedged process on Windows (reap is the only other path, and it'slsof-gated per refactor(cite): rank+fingerprint citation ids + uninstall subcommand #2).Suggested fix
start)case, whenport_occupiedbut! backend_healthyand the holder cmdline matches our reflexio/uvicorn patterns, reap it and retry the bind (reusereap_port_listeners), instead ofemit_ok; exit 0. Keep the current "foreign holder -> skip" behavior for non-matching cmdlines.reap_port_listeners/port_holdera Windows fallback whenlsofis missing (netstat -anoto map port->PID,tasklistto match the command,taskkill /F /PID).Environment
Related: #107 (same file,
_lib.sh/backend-service.shservice-startup robustness). Happy to open a PR.