Skip to content

fix(keeper): roll back partial state when Beacon.start() bind fails (gap-hunt round 2) - #3

Open
SuperInstance wants to merge 1 commit into
readme-audit-2026-07-10from
gap-hunt-2026-07-11
Open

fix(keeper): roll back partial state when Beacon.start() bind fails (gap-hunt round 2)#3
SuperInstance wants to merge 1 commit into
readme-audit-2026-07-10from
gap-hunt-2026-07-11

Conversation

@SuperInstance

Copy link
Copy Markdown
Owner

Gap-hunt pass (opencode), independently re-verified by me (rebuilt on the exact pushed commit, reran the full suite: 18/18 pass; reverted the fix locally and confirmed the new regression test genuinely fails on pre-fix code).

Bug: Beacon.start() set self._running = True before calling sock.bind(). If the bind fails (e.g. an out-of-range port, which Python's socket module raises as OverflowError — notably not a subclass of OSError), the object is left permanently bricked: start() early-returns whenever self._running is already True, so a failed bind could never be retried, and the socket itself was never cleaned up.

Fix: moved self._running = True to after a successful bind(); on failure, catches both OSError and OverflowError, clears self._sock, and closes the socket before re-raising — so the Beacon is left in a clean, retryable state rather than a half-initialized one.

Verification (independently redone):

  • Pulled the exact pushed commit, reran PYTHONPATH=src python3 -m pytest tests/ -v myself: 18/18 pass.
  • Reverted just keeper.py to the pre-fix version and reran the new test_start_failure_does_not_latch test alone — it genuinely fails on old code (assert True is False_running stayed latched True), confirming it's a real regression guard, not a trivial pass.
  • Confirmed flake8 . --count --select=E9,F63,F7,F82 (the real CI gate) is clean; the broader style-only flake8 pass shows pre-existing warnings unrelated to this change.

Beacon.start() set self._running = True before sock.bind(), so a failed
bind (bad port, in use, permission denied) latched the flag True and
left self._sock pointing at an open-but-unusable socket. The object was
then bricked: subsequent start() calls early-return on the latched flag,
no listener thread exists, and the socket leaks until GC.

Defer setting _running until bind succeeds, and on failure close + clear
the socket. OverflowError (raised for out-of-range ports) is not a
subclass of OSError, so it is caught explicitly.

Adds test_start_failure_does_not_latch covering the post-failure state
and instance reusability.
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.

1 participant