{"record":{"id":"f452418359e11015","repo":"python/cpython","slug":"expect-a-list-of-futures-not-type-fs-name","errorCode":null,"errorMessage":"expect a list of futures, not {type(fs).__name__}","messagePattern":"expect a list of futures, not (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/tasks.py","lineNumber":420,"sourceCode":"ALL_COMPLETED = concurrent.futures.ALL_COMPLETED\n\n\nasync def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):\n    \"\"\"Wait for the Futures or Tasks given by fs to complete.\n\n    The fs iterable must not be empty.\n\n    Returns two sets of Future: (done, pending).\n\n    Usage:\n\n        done, pending = await asyncio.wait(fs)\n\n    Note: This does not raise TimeoutError! Futures that aren't done\n    when the timeout occurs are returned in the second set.\n    \"\"\"\n    if futures.isfuture(fs) or coroutines.iscoroutine(fs):\n        raise TypeError(f\"expect a list of futures, not {type(fs).__name__}\")\n    if not fs:\n        raise ValueError('Set of Tasks/Futures is empty.')\n    if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):\n        raise ValueError(f'Invalid return_when value: {return_when}')\n\n    fs = set(fs)\n\n    if any(coroutines.iscoroutine(f) for f in fs):\n        raise TypeError(\"Passing coroutines is forbidden, use tasks explicitly.\")\n\n    loop = events.get_running_loop()\n    return await _wait(fs, timeout, return_when, loop)\n\n\ndef _release_waiter(waiter, *args):\n    if not waiter.done():\n        waiter.set_result(None)\n","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/tasks.py#L402-L438","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ndone, pending = await asyncio.wait(slow_op())\n\n// after\ndone, pending = await asyncio.wait([asyncio.ensure_future(slow_op())])","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"import asyncio, collections.abc\n\ndef is_awaitable_collection(fs) -> bool:\n    return isinstance(fs, collections.abc.Iterable) and not isinstance(fs, (asyncio.Future, asyncio.Task))\n\ndef to_task_set(fs):\n    items = [fs] if not isinstance(fs, collections.abc.Iterable) or isinstance(fs, (asyncio.Future,)) else list(fs)\n    return [asyncio.ensure_future(i) for i in items]","tryCatchPattern":"null","preventionTips":["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"],"tags":["asyncio","tasks","wait","type-error"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}