python/cpython · error · TypeError
expect a list of futures, not {type(fs).__name__}
Error message
expect a list of futures, not {type(fs).__name__} What it means
TypeError raised by asyncio.wait() when its first argument is a single Future or coroutine object rather than an iterable of them. wait() operates on collections; a bare awaitable is almost certainly a mistake (usually a forgotten wrapper), and passing a single coroutine would also be silently mis-iterated, so it is rejected with the offending type name.
Source
Thrown at Lib/asyncio/tasks.py:420
ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
"""Wait for the Futures or Tasks given by fs to complete.
The fs iterable must not be empty.
Returns two sets of Future: (done, pending).
Usage:
done, pending = await asyncio.wait(fs)
Note: This does not raise TimeoutError! Futures that aren't done
when the timeout occurs are returned in the second set.
"""
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
raise TypeError(f"expect a list of futures, not {type(fs).__name__}")
if not fs:
raise ValueError('Set of Tasks/Futures is empty.')
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
raise ValueError(f'Invalid return_when value: {return_when}')
fs = set(fs)
if any(coroutines.iscoroutine(f) for f in fs):
raise TypeError("Passing coroutines is forbidden, use tasks explicitly.")
loop = events.get_running_loop()
return await _wait(fs, timeout, return_when, loop)
def _release_waiter(waiter, *args):
if not waiter.done():
waiter.set_result(None)
View on GitHub (pinned to bc6749cc3b)
Solutions
- Wrap single awaitables in a list: await asyncio.wait([task])
- Prefer asyncio.gather for 'await all of these' semantics and asyncio.wait for set-based done/pending control — often the call should not be wait() at all
- Normalize inputs upstream: always build a list of tasks before calling wait
Example fix
// before done, pending = await asyncio.wait(slow_op()) // after done, pending = await asyncio.wait([asyncio.ensure_future(slow_op())])
Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
import asyncio, collections.abc
def is_awaitable_collection(fs) -> bool:
return isinstance(fs, collections.abc.Iterable) and not isinstance(fs, (asyncio.Future, asyncio.Task))
def to_task_set(fs):
items = [fs] if not isinstance(fs, collections.abc.Iterable) or isinstance(fs, (asyncio.Future,)) else list(fs)
return [asyncio.ensure_future(i) for i in items] Try / catch
null
Prevention
- Always pass a list literal or list variable to asyncio.wait
- Convert single awaitables with ensure_future and wrap in a list
- Prefer gather() unless you need done/pending sets
When it happens
Trigger: asyncio.wait(task) instead of asyncio.wait([task]); asyncio.wait(coro) with a coroutine object; passing a single future returned by a helper when only one item is pending.
Common situations: Dynamic gather sites where the list sometimes has one element and a fast path passes it unwrapped; refactors from asyncio.wait_for or await that left the single awaitable bare; copy-paste between wait() and gather() signatures.
Related errors
- a coroutine was expected, got {coro!r}
- Set of Tasks/Futures is empty.
- offset must be a non-negative integer (got {!r})
- sslcontext is expected to be an instance of ssl.SSLContext,
- string is expected
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/f452418359e11015.
Report an issue: GitHub.