Skip to main content

How to Resolve Python RuntimeWarning: "Enable tracemalloc" and Unawaited Coroutines

Encountering the RuntimeWarning: Enable tracemalloc to get the object allocation traceback in asyncio code can be confusing. While the warning suggests using the tracemalloc module, the underlying issue is almost always a different RuntimeWarning: coroutine '...' was never awaited.

This guide explains why these warnings appear together and how to fix the root cause – the missing await.

Understanding the Warnings: Unawaited Coroutines

In Python's asyncio, when you call an async def function or certain asyncio library functions (like asyncio.sleep), they return a coroutine object. This object represents the asynchronous operation but doesn't execute it immediately.

To run the coroutine and get its result (or wait for it to finish), you must use the await keyword from within another async function.

If you call a coroutine function but forget to await it:

  1. A coroutine object is created but never scheduled or run by the event loop.
  2. Python's garbage collector eventually finds this unreferenced, unrun coroutine object.
  3. This triggers the RuntimeWarning: coroutine '...' was never awaited.
  4. As a secondary effect, because an object (the coroutine) was created but potentially seems unused or improperly handled, Python also often suggests enabling tracemalloc (RuntimeWarning: Enable tracemalloc...). tracemalloc is a debugging tool to trace memory allocations, which could help find where the unawaited coroutine object was created if the location wasn't obvious.

Key Point: The tracemalloc warning is a symptom. The actual problem you need to fix is the missing await.

Illustrative Error Example

This code demonstrates the warning caused by forgetting await before asyncio.sleep():

# Error Example
import asyncio

async def my_coroutine():
print('tutorial')
# ⛔️ RuntimeWarning: coroutine 'sleep' was never awaited
# ⛔️ RuntimeWarning: Enable tracemalloc... (often follows)
asyncio.sleep(0.5) # <--- Forgot 'await' here!
print('reference.com')

async def main():
task = asyncio.create_task(my_coroutine())
await task # Wait for the task wrapping my_coroutine

# Using asyncio.run() to manage the loop
asyncio.run(main())

# Output might show 'tutorial' then 'reference.com' immediately,
# along with the RuntimeWarnings, because sleep wasn't awaited.

The Solution: Add the await Keyword

The fix is straightforward: add the await keyword before any call that returns a coroutine object you intend to run.

# Corrected Example
import asyncio

async def my_coroutine():
print('tutorial')
await asyncio.sleep(0.5) # Added 'await'
print('reference.com')

async def main():
task = asyncio.create_task(my_coroutine())
await task

asyncio.run(main())

Output:

tutorial
(pauses for ~0.5 seconds)
reference.com
  • By adding await before asyncio.sleep(0.5), we correctly pause my_coroutine and allow the event loop to run the sleep operation. The warnings disappear.

Common Scenarios for Missing await

Ensure you await in these situations:

Awaiting asyncio Functions

Many built-in asyncio functions return coroutines and must be awaited:

  • asyncio.sleep()
  • asyncio.wait()
  • asyncio.gather()
  • asyncio.open_connection()
  • Methods on streams like stream.read(), stream.write()
  • And many others...

Awaiting Custom async def Functions

Any function you define using async def returns a coroutine object when called and must be awaited from within another async function.

# Error Example
import asyncio

async def greet(name):
print(f"(Running greet for {name})")
await asyncio.sleep(0.1) # Simulate work
return f'hello {name}'

async def main():
# ⛔️ RuntimeWarning: coroutine 'greet' was never awaited
# ⛔️ RuntimeWarning: Enable tracemalloc...
result_coro = greet('tom nolan') # Forgot await! result_coro is a coroutine object
print(f"Result type: {type(result_coro)}") # Will show <class 'coroutine'>
# Trying to use the result directly won't work as expected
# print(result_coro) # Would just print the coroutine object representation

asyncio.run(main())

# Corrected Example
import asyncio

async def greet(name):
print(f"(Running greet for {name})")
await asyncio.sleep(0.1)
return f'hello {name}'

async def main():
# ✅ Correctly await the coroutine
result_str = await greet('tom nolan')
print(result_str) # Output: hello tom nolan

asyncio.run(main())

What About tracemalloc?

tracemalloc is a powerful standard library module for debugging memory usage and tracking where objects are allocated.

While Python suggests it when an unawaited coroutine is detected (because an "unused" object was created), enabling tracemalloc will not fix the underlying missing await problem.

It's merely a hint that might help you locate the source of the unawaited coroutine if it's deeply nested or hard to find. Focus on finding and adding the missing await.

Calling Async Functions from Synchronous Code

You can not use await directly in regular synchronous functions. To run an async function from synchronous code, use asyncio.run():

import asyncio

async def greet(name):
await asyncio.sleep(0.1)
return f'hello {name}'

# Synchronous context
if __name__ == "__main__":
# Use asyncio.run() to execute the async function
message = asyncio.run(greet('tom nolan'))
print(message) # Output: hello tom nolan

Conclusion

The RuntimeWarning: Enable tracemalloc... in asyncio is almost always a secondary warning triggered by the primary issue: a forgotten await keyword.

  • Focus on identifying where you called an async function or an awaitable asyncio function without preceding it with await.
  • Correcting this will resolve both warnings and ensure your asynchronous code executes correctly.