RuntimeWarning: coroutine 'fetch_user' was never awaited

Python · updated 2026-11-03

Printed as a warning when a coroutine object is garbage-collected without having been awaited. It is a warning, not an exception, so the program keeps going with a coroutine where you expected a result, and the real error shows up later.

/app/handlers.py:14: RuntimeWarning: coroutine 'fetch_user' was never awaited
  user = fetch_user(user_id)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
  File "/app/handlers.py", line 15, in handle
    return user["email"]
TypeError: 'coroutine' object is not subscriptable

Quick fix

Add await in front of the call: user = await fetch_user(user_id). That requires the calling function to be async too. If you want it to run in the background, use asyncio.create_task(...) and keep a reference to the task.

  1. Go to the file and line in the warning: that call is missing await.
  2. Add await, and make the enclosing function async if it is not already.
  3. From synchronous code (a script, a CLI), run it with asyncio.run(main()).
  4. For fire-and-forget work, use asyncio.create_task() and store the task so it is not garbage-collected.
  5. Turn on asyncio debug mode (PYTHONASYNCIODEBUG=1) to see where coroutines were created.
python
import asyncio

async def handle(user_id: str) -> str:
    user = await fetch_user(user_id)      # before: user = fetch_user(user_id)
    return user["email"]

background: set[asyncio.Task] = set()

def schedule_audit(user_id: str) -> None:
    task = asyncio.create_task(write_audit(user_id))
    background.add(task)                  # keep a reference
    task.add_done_callback(background.discard)

Why it happens

Calling an async function like a normal one

fetch_user(id) without await returns a coroutine object. Nothing runs, and code using the result fails later with a confusing TypeError.

Converting code to async halfway

When a function becomes async def, every caller needs await. Callers missed during the change keep compiling and only warn at runtime.

Callbacks and hooks that expect sync functions

Passing an async function to map(), a sort key, a signal handler or a framework hook that calls it without awaiting creates coroutines that never run.

Mocks of async functions in tests

Patching an async function with a regular Mock (instead of AsyncMock) or forgetting await in the test gives the same warning.

Other fixes

  • Enable a linter rule for unawaited coroutines (ruff and pylint both detect common cases).
  • Run tests with -W error::RuntimeWarning so this warning fails the test instead of passing silently.

Same error, other wording

  • RuntimeWarning: coroutine 'AsyncClient.get' was never awaited
  • TypeError: 'coroutine' object is not subscriptable
  • AttributeError: 'coroutine' object has no attribute 'json'

Let your coding agent catch the next one

Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:

> Find the 'coroutine was never awaited' warnings and the errors that followed them in production, show me each call site, and add the missing await or create_task.

Plans from $29/moSee what your agent readsSee the setupCancel any time from the billing portal

Related errors

References: Python docs: coroutines and tasks, Python docs: developing with asyncio (debug mode)