From 211906d7990759ea3b43fbcbd5d35ceabc6b3185 Mon Sep 17 00:00:00 2001 From: Lin Junrong Date: Mon, 17 Aug 2026 15:18:22 +0800 Subject: [PATCH] fix: keep references to lifespan background tasks (#28053) 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 4b7622a833..9c5f538562 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -382,12 +382,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: @@ -468,6 +468,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')