{"record":{"id":"0f9108e14ebd57c1","repo":"python/cpython","slug":"passing-coroutines-is-forbidden-use-tasks-explici","errorCode":null,"errorMessage":"Passing coroutines is forbidden, use tasks explicitly.","messagePattern":"Passing coroutines is forbidden, use tasks explicitly\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/tasks.py","lineNumber":429,"sourceCode":"\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\n\nasync def wait_for(fut, timeout):\n    \"\"\"Wait for the single Future or coroutine to complete, with timeout.\n\n    Coroutine will be wrapped in Task.\n\n    Returns result of the Future or coroutine.  When a timeout occurs,\n    it cancels the task and raises TimeoutError.  To avoid the task\n    cancellation, wrap it in shield().","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/tasks.py#L411-L447","documentation":"Raised by asyncio.wait() when any element of the passed collection is a bare coroutine object. Since Python 3.8 (deprecated) and 3.11 (enforced), asyncio.wait() requires real Task/Future objects so that the caller keeps a strong reference and can manage cancellation; passing raw coroutines risks them being garbage-collected mid-wait.","triggerScenarios":"Calling `await asyncio.wait([my_coro(), other_coro()])` where the list items are coroutine objects, not Tasks. The check `any(coroutines.iscoroutine(f) for f in fs)` fires before waiting begins.","commonSituations":"Porting old asyncio code from Python <=3.7 to 3.11+; mixing asyncio.gather-style idioms (which wraps coroutines for you) with asyncio.wait; dynamically building a list of awaitables from functions that return coroutines.","solutions":["Wrap each coroutine with asyncio.create_task() before passing: `tasks = [asyncio.create_task(c()) for c in coros]; await asyncio.wait(tasks)`","Or use asyncio.gather(*coros) if you only need results, since it accepts coroutines directly","Or use asyncio.as_completed([asyncio.create_task(c) for c in coros]) if you need 'first done' ordering","If supporting multiple versions, use asyncio.ensure_future() which is a no-op on Tasks and wraps coroutines"],"exampleFix":"// before\nawait asyncio.wait([fetch('a'), fetch('b')])\n\n// after\ntasks = [asyncio.create_task(fetch('a')), asyncio.create_task(fetch('b'))]\ndone, pending = await asyncio.wait(tasks)","handlingStrategy":"validation","validationCode":"import asyncio, inspect\n\ncoros = [fetch('a'), fetch('b')]\nassert not any(inspect.iscoroutine(c) for c in coros) or True  # detect the condition\nfs = [asyncio.ensure_future(c) for c in coros]  # now all Tasks/Futures\ndone, pending = await asyncio.wait(fs)","typeGuard":"import asyncio\n\ndef is_waitable_item(obj) -> bool:\n    \"\"\"True if asyncio.wait() accepts obj directly.\"\"\"\n    return isinstance(obj, (asyncio.Future, asyncio.Task))","tryCatchPattern":"try:\n    await asyncio.wait(items)\nexcept TypeError as e:\n    if 'coroutines is forbidden' in str(e):\n        items = [asyncio.ensure_future(i) for i in items]\n        await asyncio.wait(items)\n    else:\n        raise","preventionTips":["Always build the wait list with asyncio.create_task()/ensure_future at creation time","Keep strong references to the created Tasks for the duration of the wait","Prefer asyncio.gather unless you truly need the done/pending set split","Run tests on the oldest and newest Python you support to catch 3.8->3.11 behavior changes"],"tags":["asyncio","python","coroutine","tasks","concurrency"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}