Restarting the webui/daemon always required waiting ~1 minute: the fail-fast port pre-check in server.py bound a plain socket without SO_REUSEADDR, so TIME_WAIT sockets left by a just-stopped instance made it report "port already in use" — even though uvicorn (which sets SO_REUSEADDR itself, for both asyncio and uvloop) would have bound fine.
Fix
Set SO_REUSEADDR on the pre-check socket, mirroring uvicorn's actual bind semantics. The check now only fails when the port is genuinely held by a live listener (on Linux, SO_REUSEADDR does not permit two live listeners — that requires SO_REUSEPORT), so fail-fast behavior is preserved.
Verification
Reproduced the scenario: with a TIME_WAIT socket on the port, the old check fails (EADDRINUSE), the new check binds OK
A live listener on the port is still correctly rejected (no false negatives)
Full test suite: 191 passed, 0 failed
## Problem
Restarting the webui/daemon always required waiting ~1 minute: the fail-fast port pre-check in `server.py` bound a plain socket without `SO_REUSEADDR`, so `TIME_WAIT` sockets left by a just-stopped instance made it report "port already in use" — even though uvicorn (which sets `SO_REUSEADDR` itself, for both asyncio and uvloop) would have bound fine.
## Fix
Set `SO_REUSEADDR` on the pre-check socket, mirroring uvicorn's actual bind semantics. The check now only fails when the port is genuinely held by a live listener (on Linux, `SO_REUSEADDR` does not permit two live listeners — that requires `SO_REUSEPORT`), so fail-fast behavior is preserved.
## Verification
- Reproduced the scenario: with a `TIME_WAIT` socket on the port, the old check fails (`EADDRINUSE`), the new check binds OK
- A live listener on the port is still correctly rejected (no false negatives)
- Full test suite: 191 passed, 0 failed
The fail-fast port check bound a plain socket, so TIME_WAIT sockets left
by a just-stopped instance (~60s) made it report 'port already in use'
even though uvicorn (which sets SO_REUSEADDR) could bind fine. Mirror
uvicorn's bind semantics: the check now only fails when the port is
genuinely held by a live listener.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Restarting the webui/daemon always required waiting ~1 minute: the fail-fast port pre-check in
server.pybound a plain socket withoutSO_REUSEADDR, soTIME_WAITsockets left by a just-stopped instance made it report "port already in use" — even though uvicorn (which setsSO_REUSEADDRitself, for both asyncio and uvloop) would have bound fine.Fix
Set
SO_REUSEADDRon the pre-check socket, mirroring uvicorn's actual bind semantics. The check now only fails when the port is genuinely held by a live listener (on Linux,SO_REUSEADDRdoes not permit two live listeners — that requiresSO_REUSEPORT), so fail-fast behavior is preserved.Verification
TIME_WAITsocket on the port, the old check fails (EADDRINUSE), the new check binds OK