python/cpython · error · TypeError
a coroutine was expected, got {coro!r}
Error message
a coroutine was expected, got {coro!r} What it means
TypeError raised by Task.__init__ when the first argument is not a coroutine object (checked with coroutines.iscoroutine). Tasks wrap exactly one coroutine; passing anything else — an async function itself, a plain function, a generator, a Future — is rejected immediately (with pending-coroutine logging disabled since nothing valid exists to destroy).
Source
Thrown at Lib/asyncio/tasks.py:93
# as it schedules __wakeup() to be called (which calls __step() so
# we way that __step() is scheduled).
# * It transitions from 2 to 3 when __step() is executed, and it clears
# _fut_waiter to None.
# If False, don't log a message if the task is destroyed while its
# status is still pending
_log_destroy_pending = True
def __init__(self, coro, *, loop=None, name=None, context=None,
eager_start=False):
super().__init__(loop=loop)
if self._source_traceback:
del self._source_traceback[-1]
if not coroutines.iscoroutine(coro):
# raise after Future.__init__(), attrs are required for __del__
# prevent logging for pending task in __del__
self._log_destroy_pending = False
raise TypeError(f"a coroutine was expected, got {coro!r}")
if name is None:
self._name = f'Task-{_task_name_counter()}'
else:
self._name = str(name)
self._num_cancels_requested = 0
self._must_cancel = False
self._fut_waiter = None
self._coro = coro
if context is None:
self._context = contextvars.copy_context()
else:
self._context = context
if eager_start and self._loop.is_running():
self.__eager_start()
else:View on GitHub (pinned to bc6749cc3b)
Solutions
- Call the function: create the coroutine object first — coro = my_async(); then Task(coro) / asyncio.create_task(my_async())
- For generic awaitables (Futures, custom __await__), use asyncio.ensure_future() which wraps appropriately
- Double-check higher-order APIs: registries and dispatch maps should store functions but invoke them as value() when spawning
Example fix
// before task = asyncio.create_task(poll_service) # function, not coroutine // after task = asyncio.create_task(poll_service())
Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
import inspect
def spawn(loop, awaitable):
if inspect.iscoroutinefunction(awaitable):
raise TypeError('forgot to call: pass the coroutine object, not the function')
if not inspect.iscoroutine(awaitable):
return asyncio.ensure_future(awaitable)
return loop.create_task(awaitable) Try / catch
null
Prevention
- Always write the call: create_task(my_func())
- Use ensure_future for generic awaitables, Task/create_task only for coroutine objects
- Lint for create_task identifiers not followed by '(' inside its argument
When it happens
Trigger: asyncio.Task(my_async_func) instead of asyncio.Task(my_async_func()); same for loop.create_task(async_func); passing a coroutine function reference stored in a variable; passing a functools.partial of an async function without calling it; on non-default loops, types registered with a different event loop policy may also fail iscoroutine.
Common situations: Forgetting the parentheses — the most common form; passing a callback registry value that is a function not a coroutine object; mixing awaitable objects (Futures, objects with __await__) that are not coroutines and must go through ensure_future; refactors from callbacks to async that left a call operator behind.
Related errors
- Task does not support set_result operation
- Task does not support set_exception operation
- expect a list of futures, not {type(fs).__name__}
- offset must be a non-negative integer (got {!r})
- sslcontext is expected to be an instance of ssl.SSLContext,
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/e874ee44c15f5dc5.
Report an issue: GitHub.