"Asyncio create_task Garbage Collected: Fix It Fast"

"If your asyncio create_task fire-and-forget background work randomly never finishes, the task is probably being garbage collected. This guide shows the exact failure, why the event loop only holds weak references, the three-line background_tasks set fix, how to verify it, and the rules that stop it…

Your analytics write works perfectly on your laptop. In production, under load, it silently stops happening. No error, no traceback, no log line — the background work just never completes. If this sounds familiar, you&#x27;ve probably been bitten by the asyncio create_task garbage collected bug: fire-and-forget tasks whose Task object nobody references can be destroyed by the garbage collector *while they&#x27;re still running*. What the error looks like There are two failure modes, and the quiet one is worse. Failure mode 1 — the warning. A fire-and-forget task raises an exception, nobody ever retrieves it, and at some later point Python prints this to stderr: Task exception was never retrieved future: <Task finished name=&#x27;Task-2&#x27; coro=<broken() done, defined at demo.py:3> exception=RuntimeError(&#x27;boom&#x27;)> You can reproduce it with this tiny script: import asyncio async def broken(): raise RuntimeError("boom") async def main(): asyncio.create_task(broken()) # nobody keeps the Task object await asyncio.sleep(0.1) asyncio.run(main()) The warning arrives at GC time, far from the cause, and in production stderr it scrolls past unnoticed. Failure mode 2 — silence. The task doesn&#x27;t raise; it just never finishes. A background sync, a webhook retry, a metrics flush — scheduled with a bare asyncio.create_task(...) statement, the Task handle is dropped, and under allocation pressure the collector finalizes it mid-await. The coroutine stops resuming. Nothing is logged. Your monitoring sees the request succeed while the side effect quietly never happened. Why the asyncio create_task garbage collected bug happens asyncio.create_task() returns a Task object, and the event loop keeps only weak references to the tasks it runs. The loop&#x27;s scheduled callback…

Explore