Skip to content

Fix #840: Use SO_REUSEADDR in port probe to ignore TIME_WAIT#845

Open
kishansaaai wants to merge 2 commits into
repowise-dev:mainfrom
kishansaaai:fix-840-time-wait-port
Open

Fix #840: Use SO_REUSEADDR in port probe to ignore TIME_WAIT#845
kishansaaai wants to merge 2 commits into
repowise-dev:mainfrom
kishansaaai:fix-840-time-wait-port

Conversation

@kishansaaai

Copy link
Copy Markdown
Contributor

Summary

This PR fixes the issue where repowise serve would silently fallback to an alternative port (e.g. 7338 or 3001) during quick restarts due to the preferred ports being temporarily held in a TIME_WAIT state by the operating system.

Root Cause

The _is_port_free check used a plain socket.bind() probe. When a previous instance of the server exits, the listening sockets linger in a TIME_WAIT state for ~60s. A standard probe treats these ports as "in use", even though actual web servers (like uvicorn and Next.js) configure SO_REUSEADDR to safely rebind them immediately.

This resulted in a silent fallback that broke the UI proxy configuration, causing an ECONNREFUSED error loop as the Next.js UI continued attempting to reach the API on the preferred port (7337) while the backend API was stealthily moved to 7338.

Changes

  • Modified _is_port_free in packages/cli/src/repowise/cli/commands/serve_cmd.py to enable SO_REUSEADDR on POSIX systems (os.name != "nt"). This accurately aligns the pre-flight probe with the real bind semantics of the downstream servers.
  • Appended a regression test test_is_port_free_ignores_time_wait in tests/unit/cli/test_serve_port_fallback.py to assert that TIME_WAIT states (simulated via an active close sequence) are properly ignored by the probe.

Related Issues

Fixes #840

Test Plan

  • Run pytest tests/unit/cli/test_serve_port_fallback.py to verify the regression test logic locally.
  • Tested clean binding behavior when forcefully stopping and starting the server consecutively.

On restart, sockets can remain in TIME_WAIT, preventing serve_cmd._is_port_free() from recognizing the port as available. This sets SO_REUSEADDR on POSIX systems so the probe matches uvicorn and Next.js behavior and correctly reclaims the preferred ports.
@repowise-bot

repowise-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

✅ Health: 7.6 (unchanged)

📋 At a glance
1 hotspot touched · 5 co-change pairs left out · 2 files with recent fix history.

Files & modules (2)
  • packages (1 file)
    • .../commands/serve_cmd.py
  • tests (1 file)
    • .../cli/test_serve_port_fallback.py

🩹 Review priority (files here with the most recent bug-fix history — defects cluster, so review these first)

🔎 More signals (2)

🔥 Hotspot touched (1)

  • .../commands/serve_cmd.py — 10 commits/90d, 2 dependents · primary owner: Raghav Chamadiya (75%)

🔗 Hidden coupling (1 file)

  • .../commands/serve_cmd.py co-changes with these files (not in this PR):
    • .../cli/helpers.py (4× — 🟢 routine)
    • pyproject.toml (4× — 🟢 routine)
    • .../server/provider_config.py (3× — 🟢 routine)
    • .../routers/git.py (3× — 🟢 routine)
    • .../server/app.py (3× — 🟢 routine)

📊 Full report · ⭐ Star Repowise · 📥 Install bot · Last updated 2026-07-16 18:25 UTC
Silence on a single PR with [skip repowise] in the title · Per-repo toggle on repowise.dev/settings?tab=bot

@RaghavChamadiya

Copy link
Copy Markdown
Member

Nice fix, the root-cause analysis is spot on and guarding SO_REUSEADDR behind os.name != "nt" is the right call. The production change looks good and I confirmed it does not regress test_is_port_free_detects_busy_port (a live listen()ing socket still blocks the probe on Linux).

One thing to fix before merge: the new test fails on Windows. test_is_port_free_ignores_time_wait asserts _is_port_free(...) is True, but since the fix intentionally skips SO_REUSEADDR on Windows, binding a TIME_WAIT port there fails and the probe returns False. CI is Linux-only so the gate stays green, but this repo gets developed on Windows and a local pytest run there would hit a hard failure.

Please guard it so the test matches the POSIX-only scope of the fix:

@pytest.mark.skipif(os.name == "nt", reason="SO_REUSEADDR probe fix is POSIX-only (issue #840)")
def test_is_port_free_ignores_time_wait() -> None:
    ...

That also documents why the behavior is POSIX-scoped. Thanks for the clean writeup on the issue!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] serve: preferred ports (7337/3000) not reclaimed on restart — TIME_WAIT trips a silent fallback that breaks the web UI

2 participants