Summary
start_gateway() starts the background cron ticker thread, waits for shutdown, and then returns early on runner.should_exit_with_failure. That early return skips cron_stop.set() and cron_thread.join(...), leaving the cron ticker running after a failure exit path.
Affected code
Why this is a bug
On the failure shutdown path, start_gateway() returns False before executing the cron cleanup block. This leaks the daemon cron thread for embedded/test/library callers and can keep scheduled work firing until the process exits.
Minimal reproduction
# fake runner:
# - start() -> True
# - wait_for_shutdown() returns
# - should_exit_with_failure = True
# - exit_reason = "boom"
result = await start_gateway(None)
print(result, thread_started, thread_joined)
# actual: False, True, False
Observed with a patched fake thread:
thread_started = True
thread_joined = False
Expected behavior
- The cron ticker should always be stopped/joined before
start_gateway() returns, regardless of whether shutdown is clean or failure-driven.
Actual behavior
- Failure exit returns before cleanup, so the ticker thread keeps running.
Suggested investigation
- Move cron cleanup into a
finally block after wait_for_shutdown() or otherwise guarantee cleanup on both clean and failure return paths.
- Add a focused test for
should_exit_with_failure=True that asserts the cron thread is joined.
Summary
start_gateway()starts the background cron ticker thread, waits for shutdown, and then returns early onrunner.should_exit_with_failure. That early return skipscron_stop.set()andcron_thread.join(...), leaving the cron ticker running after a failure exit path.Affected code
gateway/run.py:8941-8963Why this is a bug
On the failure shutdown path,
start_gateway()returnsFalsebefore executing the cron cleanup block. This leaks the daemon cron thread for embedded/test/library callers and can keep scheduled work firing until the process exits.Minimal reproduction
Observed with a patched fake thread:
thread_started = Truethread_joined = FalseExpected behavior
start_gateway()returns, regardless of whether shutdown is clean or failure-driven.Actual behavior
Suggested investigation
finallyblock afterwait_for_shutdown()or otherwise guarantee cleanup on both clean and failure return paths.should_exit_with_failure=Truethat asserts the cron thread is joined.