_run_startup_recovery() creates a separate passes task and waits on it through asyncio.shield(). In the stop path at lionagi/studio/scheduler/engine.py:744-747, it calls passes.cancel() and immediately re-raises without awaiting the task termination. If a recovery operation performs cancellation cleanup or otherwise delays cancellation, the outer tick task finishes and stop() continues to close the scheduler state service while passes is still using it. This leaves a background recovery task racing shutdown and can produce post-shutdown database errors or incomplete recovery.
Drain the passes task after requesting cancellation before allowing stop() to continue closing the state service; preserve the non-stop path shielding so ordinary stray cancels still let recovery finish.
Introduced by PR #3381. Relevant code: engine.py#L744-L747.
_run_startup_recovery()creates a separatepassestask and waits on it throughasyncio.shield(). In the stop path atlionagi/studio/scheduler/engine.py:744-747, it callspasses.cancel()and immediately re-raises without awaiting the task termination. If a recovery operation performs cancellation cleanup or otherwise delays cancellation, the outer tick task finishes andstop()continues to close the scheduler state service whilepassesis still using it. This leaves a background recovery task racing shutdown and can produce post-shutdown database errors or incomplete recovery.Drain the
passestask after requesting cancellation before allowingstop()to continue closing the state service; preserve the non-stop path shielding so ordinary stray cancels still let recovery finish.Introduced by PR #3381. Relevant code: engine.py#L744-L747.