Skip to content

Keep the supervisor alive when the runtime state file is briefly locked (Windows WinError 5) - #92

Open
syrac88 wants to merge 1 commit into
lukaszliniewicz:mainfrom
syrac88:fix-runtime-state-replace-race
Open

Keep the supervisor alive when the runtime state file is briefly locked (Windows WinError 5)#92
syrac88 wants to merge 1 commit into
lukaszliniewicz:mainfrom
syrac88:fix-runtime-state-replace-race

Conversation

@syrac88

@syrac88 syrac88 commented Jul 18, 2026

Copy link
Copy Markdown

Fixes #91.

What: _write_state() now retries os.replace() up to 10× (100 ms apart) and, if the destination stays locked, logs and skips the update instead of raising. The orphaned .tmp file is cleaned up. Two regression tests cover the transient-failure and persistent-failure paths.

Why: On Windows, any concurrent reader of runtime-processes.json (e.g. the GUI's 2-second status poll) blocks os.replace() with WinError 5 because Python's open() doesn't share FILE_SHARE_DELETE. The uncaught exception propagated through monitor_once()run_foreground() and tore down every supervised process mid-run ("all processes have exited"). Since the state file is informational and rewritten every tick, a skipped update is harmless — crashing the whole app over it is not.

Testing: python -m unittest tests.test_web_supervisor — 9 tests pass (2 new). Battle-tested on the affected machine (Windows 11, long Chatterbox audiobook runs): multiple crashes per day before, many hours of stable generation after.

On Windows, Python's open() shares files without FILE_SHARE_DELETE, so any
concurrent reader of runtime-processes.json (for example the installer
GUI's 2-second status poll) makes the supervisor's os.replace() in
_write_state() fail with WinError 5. The exception propagated through
monitor_once() and run_foreground(), tearing down every supervised
process ("all processes have exited") in the middle of long TTS runs.

Retry the replace briefly; if the file stays locked, log and skip the
update instead of crashing. The state file is informational and is
rewritten on the next monitor tick, so a skipped update is harmless.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqeQcimSQTFvLe3NmnL5sF
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.

Supervisor crashes with "all processes have exited" — WinError 5 race on runtime-processes.json (Windows)

1 participant