[{"data":1,"prerenderedAt":487},["ShallowReactive",2],{"page-\u002Fsystematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002Ftracing-unawaited-coroutine-warnings\u002F":3},{"id":4,"title":5,"body":6,"description":456,"extension":457,"meta":458,"navigation":146,"path":483,"seo":484,"stem":485,"__hash__":486},"content\u002Fsystematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002Ftracing-unawaited-coroutine-warnings\u002Findex.md","Tracing \"coroutine was never awaited\" Warnings",{"type":7,"value":8,"toc":449},"minimark",[9,25,30,70,74,80,128,131,210,216,224,227,254,268,272,304,308,386,390,402,419,438,445],[10,11,12,16,17,20,21,24],"p",{},[13,14,15],"code",{},"RuntimeWarning: coroutine '...' was never awaited"," is one of the most misleading messages in asyncio: it fires when the orphaned coroutine is garbage-collected, so the traceback points at GC internals or an unrelated line, not the missing ",[13,18,19],{},"await",". The coroutine never ran, so whatever side effect you expected silently did not happen. This guide turns that vague late warning into a hard error with a traceback that points straight at the coroutine's creation site, using ",[13,22,23],{},"tracemalloc"," and warning filters.",[26,27,29],"h2",{"id":28},"prerequisites","Prerequisites",[31,32,33,52,59],"ul",{},[34,35,36,37,40,41,43,44,47,48,51],"li",{},"Python ",[13,38,39],{},"3.8+"," (",[13,42,23],{},", the ",[13,45,46],{},"-X tracemalloc"," flag, and ",[13,49,50],{},"warnings"," filters).",[34,53,54,55,58],{},"For mocking causes: ",[13,56,57],{},"unittest.mock.AsyncMock"," (added in Python 3.8).",[34,60,61,62,65,66,69],{},"For the pytest path: any recent ",[13,63,64],{},"pytest"," with ",[13,67,68],{},"filterwarnings"," support.",[26,71,73],{"id":72},"solution","Solution",[10,75,76,77,79],{},"Run the program with the warning promoted to an error and ",[13,78,23],{}," enabled so the message carries the allocation traceback:",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-bash shiki shiki-themes github-light github-dark","# -W error::RuntimeWarning raises instead of logging late.\n# -X tracemalloc attaches the traceback to where the coroutine was created.\npython -W error::RuntimeWarning -X tracemalloc app.py\n","bash","",[13,88,89,98,104],{"__ignoreMap":86},[90,91,94],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"sJ8bj","# -W error::RuntimeWarning raises instead of logging late.\n",[90,99,101],{"class":92,"line":100},2,[90,102,103],{"class":96},"# -X tracemalloc attaches the traceback to where the coroutine was created.\n",[90,105,107,111,115,119,122,125],{"class":92,"line":106},3,[90,108,110],{"class":109},"sScJk","python",[90,112,114],{"class":113},"sj4cs"," -W",[90,116,118],{"class":117},"sZZnC"," error::RuntimeWarning",[90,120,121],{"class":113}," -X",[90,123,124],{"class":117}," tracemalloc",[90,126,127],{"class":117}," app.py\n",[10,129,130],{},"In code, the equivalent is explicit:",[81,132,135],{"className":133,"code":134,"language":110,"meta":86,"style":86},"language-python shiki shiki-themes github-light github-dark","import asyncio, tracemalloc, warnings\n\ntracemalloc.start()                                # record allocation tracebacks\nwarnings.simplefilter(\"error\", RuntimeWarning)     # missing await -> raised error\n\nasync def save(record: dict) -> None:\n    await asyncio.sleep(0)                         # pretend to persist\n\nasync def main() -> None:\n    save({\"id\": 1})         # BUG: no await -> coroutine created but never run\n    await asyncio.sleep(0)  # yield so GC can collect the orphan and trigger the warning\n\nasyncio.run(main())\n",[13,136,137,142,148,153,159,164,170,176,181,187,193,199,204],{"__ignoreMap":86},[90,138,139],{"class":92,"line":93},[90,140,141],{},"import asyncio, tracemalloc, warnings\n",[90,143,144],{"class":92,"line":100},[90,145,147],{"emptyLinePlaceholder":146},true,"\n",[90,149,150],{"class":92,"line":106},[90,151,152],{},"tracemalloc.start()                                # record allocation tracebacks\n",[90,154,156],{"class":92,"line":155},4,[90,157,158],{},"warnings.simplefilter(\"error\", RuntimeWarning)     # missing await -> raised error\n",[90,160,162],{"class":92,"line":161},5,[90,163,147],{"emptyLinePlaceholder":146},[90,165,167],{"class":92,"line":166},6,[90,168,169],{},"async def save(record: dict) -> None:\n",[90,171,173],{"class":92,"line":172},7,[90,174,175],{},"    await asyncio.sleep(0)                         # pretend to persist\n",[90,177,179],{"class":92,"line":178},8,[90,180,147],{"emptyLinePlaceholder":146},[90,182,184],{"class":92,"line":183},9,[90,185,186],{},"async def main() -> None:\n",[90,188,190],{"class":92,"line":189},10,[90,191,192],{},"    save({\"id\": 1})         # BUG: no await -> coroutine created but never run\n",[90,194,196],{"class":92,"line":195},11,[90,197,198],{},"    await asyncio.sleep(0)  # yield so GC can collect the orphan and trigger the warning\n",[90,200,202],{"class":92,"line":201},12,[90,203,147],{"emptyLinePlaceholder":146},[90,205,207],{"class":92,"line":206},13,[90,208,209],{},"asyncio.run(main())\n",[10,211,212,213,215],{},"With ",[13,214,23],{}," on, the raised error includes:",[81,217,222],{"className":218,"code":220,"language":221,"meta":86},[219],"language-text","RuntimeWarning: coroutine 'save' was never awaited\nCoroutine created at (most recent call last):\n  File \"app.py\", line 10, in main\n    save({\"id\": 1})\n","text",[13,223,220],{"__ignoreMap":86},[10,225,226],{},"For the whole test suite, fail on it in pytest config:",[81,228,232],{"className":229,"code":230,"language":231,"meta":86,"style":86},"language-toml shiki shiki-themes github-light github-dark","# pyproject.toml\n[tool.pytest.ini_options]\nfilterwarnings = [\"error::RuntimeWarning\"]\n# run pytest with: pytest -W error::RuntimeWarning -p no:cacheprovider --tb=short\n","toml",[13,233,234,239,244,249],{"__ignoreMap":86},[90,235,236],{"class":92,"line":93},[90,237,238],{},"# pyproject.toml\n",[90,240,241],{"class":92,"line":100},[90,242,243],{},"[tool.pytest.ini_options]\n",[90,245,246],{"class":92,"line":106},[90,247,248],{},"filterwarnings = [\"error::RuntimeWarning\"]\n",[90,250,251],{"class":92,"line":155},[90,252,253],{},"# run pytest with: pytest -W error::RuntimeWarning -p no:cacheprovider --tb=short\n",[10,255,256,257,259,260,263,264,267],{},"The fix is one of: add the ",[13,258,19],{},", schedule it with ",[13,261,262],{},"asyncio.create_task(save(...))",", or include it in ",[13,265,266],{},"asyncio.gather(...)",".",[26,269,271],{"id":270},"why-this-works","Why this works",[10,273,274,275,278,279,281,282,285,286,289,290,292,293,297,298,300,301,303],{},"A coroutine object created by calling an ",[13,276,277],{},"async def"," does nothing until it is driven by an ",[13,280,19],{}," or scheduled on the loop. If the only reference is dropped, the garbage collector reclaims it and CPython emits the ",[13,283,284],{},"RuntimeWarning"," from the coroutine's ",[13,287,288],{},"__del__"," — which is why the default traceback is useless. ",[13,291,23],{}," records a traceback at every allocation, so when the warning fires asyncio can attach the ",[294,295,296],"em",{},"creation"," traceback to it, pointing at the exact call site that forgot the ",[13,299,19],{},". Promoting the warning to an error with the ",[13,302,50],{}," filter makes the failure deterministic and CI-visible instead of a line buried in logs after the program already produced wrong results.",[26,305,307],{"id":306},"edge-cases-and-failure-modes","Edge cases and failure modes",[31,309,310,337,347,356,373],{},[34,311,312,319,320,322,323,325,326,328,329,331,332,267],{},[313,314,315,316,267],"strong",{},"Mocking an async method with ",[13,317,318],{},"Mock"," A plain ",[13,321,318],{}," returns a ",[13,324,318],{},", not an awaitable, so production code that ",[13,327,19],{},"s it breaks, while test code that calls it without awaiting leaks a coroutine. Use ",[13,330,57],{}," for async methods — see ",[333,334,336],"a",{"href":335},"\u002Fadvanced-mocking-test-doubles-in-python\u002Fdeep-dive-into-unittestmock\u002Fwhen-to-use-magicmock-vs-mock-in-python\u002F","when to use MagicMock vs Mock in Python",[34,338,339,342,343,346],{},[313,340,341],{},"Coroutine passed where a value is expected."," ",[13,344,345],{},"if save(record):"," is always truthy because the coroutine object is truthy; the body never persists anything. Await first, then test the result.",[34,348,349,342,352,355],{},[313,350,351],{},"Fire-and-forget without a reference.",[13,353,354],{},"asyncio.create_task(coro)"," schedules the coroutine, but if you keep no reference the task can be GC'd mid-flight. Store the task and await it at shutdown.",[34,357,358,361,362,364,365,368,369,372],{},[313,359,360],{},"Warning suppressed by a broad filter."," A library or ",[13,363,64],{}," config with ",[13,366,367],{},"filterwarnings = [\"ignore\"]"," hides it. Add an explicit ",[13,370,371],{},"error::RuntimeWarning"," rule that takes precedence.",[34,374,375,378,379,381,382,267],{},[313,376,377],{},"Late GC hides the origin without tracemalloc."," Without ",[13,380,46],{}," the warning has no creation traceback; always pair the two. The same allocation-traceback technique underpins ",[333,383,385],{"href":384},"\u002Fsystematic-debugging-performance-profiling\u002Fmemory-profiling-with-tracemalloc\u002F","memory profiling with tracemalloc",[26,387,389],{"id":388},"frequently-asked-questions","Frequently Asked Questions",[10,391,392,395,396,398,399,401],{},[313,393,394],{},"Why does the never-awaited warning point at the wrong line?","\nThe ",[13,397,284],{}," fires when the unawaited coroutine is garbage collected, which can be far from where it was created. Enable ",[13,400,23],{}," so the warning includes the allocation traceback pointing at the coroutine's real origin.",[10,403,404,407,408,411,412,415,416,418],{},[313,405,406],{},"How do I make a missing await fail the test suite?","\nRun with ",[13,409,410],{},"-W error::RuntimeWarning",", or set ",[13,413,414],{},"filterwarnings = error::RuntimeWarning"," in pytest config, so the warning is raised as an error. Combine it with ",[13,417,46],{}," to get the allocation traceback.",[10,420,421,424,425,427,428,431,432,434,435,267],{},[313,422,423],{},"What are the most common causes of an unawaited coroutine?","\nCalling an async function without ",[13,426,19],{},", passing a coroutine where a value is expected, forgetting to await ",[13,429,430],{},"asyncio.sleep"," or a client call, and mocking an async method with a plain ",[13,433,318],{}," instead of ",[13,436,437],{},"AsyncMock",[10,439,440,441],{},"← Back to ",[333,442,444],{"href":443},"\u002Fsystematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002F","Debugging Async Code and Event Loops",[446,447,448],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":86,"searchDepth":100,"depth":100,"links":450},[451,452,453,454,455],{"id":28,"depth":100,"text":29},{"id":72,"depth":100,"text":73},{"id":270,"depth":100,"text":271},{"id":306,"depth":100,"text":307},{"id":388,"depth":100,"text":389},"Trace RuntimeWarning: coroutine was never awaited to its source with tracemalloc and -W error::RuntimeWarning, and fix the common missing-await causes.","md",{"slug":459,"type":460,"breadcrumb":461,"datePublished":462,"dateModified":462,"faq":463,"howto":470},"tracing-unawaited-coroutine-warnings","long_tail","Never-awaited coroutine","2026-06-18",[464,466,468],{"q":394,"a":465},"The RuntimeWarning fires when the unawaited coroutine is garbage collected, which can be far from where it was created. Enable tracemalloc so the warning includes the allocation traceback pointing at the coroutine's real origin.",{"q":406,"a":467},"Run with -W error::RuntimeWarning, or set filterwarnings = error::RuntimeWarning in pytest config, so the warning is raised as an error. Combine it with -X tracemalloc to get the allocation traceback.",{"q":423,"a":469},"Calling an async function without await, passing a coroutine where a value is expected, forgetting to await asyncio.sleep or a client call, and mocking an async method with a plain Mock instead of AsyncMock.",{"name":471,"description":472,"steps":473},"How to trace a coroutine was never awaited warning","Turn the late warning into a raised error with a precise traceback using tracemalloc and warning filters.",[474,477,480],{"name":475,"text":476},"Promote the warning to an error","Run with -W error::RuntimeWarning or set filterwarnings in pytest config so the missing await raises instead of logging late.",{"name":478,"text":479},"Enable tracemalloc","Start Python with -X tracemalloc or call tracemalloc.start so the warning carries the coroutine's allocation traceback.",{"name":481,"text":482},"Read the traceback to the origin","Follow the allocation traceback to the line that created the coroutine and add the missing await or schedule it as a task.","\u002Fsystematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002Ftracing-unawaited-coroutine-warnings",{"title":5,"description":456},"systematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002Ftracing-unawaited-coroutine-warnings\u002Findex","T37snX9Ltlll3Hz7Xs9ommDx8xGIM6z6BcI3SOtznjY",1781793487407]