From ecc93eadbf18f848cb30ee8f8ba3263c671e3e34 Mon Sep 17 00:00:00 2001 From: Lin Junrong Date: Mon, 3 Aug 2026 14:24:21 +0800 Subject: [PATCH] fix: keep references to lifespan background tasks periodic_usage_pool_cleanup, periodic_session_pool_cleanup and scheduler_worker_loop were started with asyncio.create_task and their handles discarded. The event loop keeps only a weak reference to a task, so a task with no other referent can be garbage collected while it is suspended at an await. All three are while True loops meant to run for the process lifetime, and if one is collected the failure is silent: pool entries stop being cleaned up, or automations and calendar alerts stop firing, with nothing logged. Six lines above, redis_task_command_listener is already stored on app.state and cancelled on shutdown. This applies the same treatment to the other three. Closes #28052 --- backend/open_webui/main.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 781a6f4a71..076f27c818 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -373,12 +373,12 @@ async def lifespan(app: FastAPI): limiter = anyio.to_thread.current_default_thread_limiter() limiter.total_tokens = THREAD_POOL_SIZE - asyncio.create_task(periodic_usage_pool_cleanup()) - asyncio.create_task(periodic_session_pool_cleanup()) + app.state.periodic_usage_pool_cleanup = asyncio.create_task(periodic_usage_pool_cleanup()) + app.state.periodic_session_pool_cleanup = asyncio.create_task(periodic_session_pool_cleanup()) from open_webui.utils.automations import scheduler_worker_loop - asyncio.create_task(scheduler_worker_loop(app)) + app.state.scheduler_worker_loop = asyncio.create_task(scheduler_worker_loop(app)) if await Config.get('models.base_models_cache'): try: @@ -459,6 +459,10 @@ async def lifespan(app: FastAPI): if hasattr(app.state, 'redis_task_command_listener'): app.state.redis_task_command_listener.cancel() + app.state.periodic_usage_pool_cleanup.cancel() + app.state.periodic_session_pool_cleanup.cancel() + app.state.scheduler_worker_loop.cancel() + await publish_event(app, EVENTS.SYSTEM_SHUTDOWN_COMPLETED, source='system')