{"record":{"id":"fbeb7a53c8f4dd53","repo":"python/cpython","slug":"a-future-is-required-for-source-argument","errorCode":null,"errorMessage":"A future is required for source argument","messagePattern":"A future is required for source argument","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/futures.py","lineNumber":381,"sourceCode":"    done, cancelled, result, exception = source._get_snapshot()\n    assert done\n    if cancelled:\n        dest.cancel()\n    elif exception is not None:\n        dest.set_exception(_convert_future_exc(exception))\n    else:\n        dest.set_result(result)\n\ndef _chain_future(source, destination):\n    \"\"\"Chain two futures so that when one completes, so does the other.\n\n    The result (or exception) of source will be copied to destination.\n    If destination is cancelled, source gets cancelled too.\n    Compatible with both asyncio.Future and concurrent.futures.Future.\n    \"\"\"\n    if not isfuture(source) and not isinstance(source,\n                                               concurrent.futures.Future):\n        raise TypeError('A future is required for source argument')\n    if not isfuture(destination) and not isinstance(destination,\n                                                    concurrent.futures.Future):\n        raise TypeError('A future is required for destination argument')\n    source_loop = _get_loop(source) if isfuture(source) else None\n    dest_loop = _get_loop(destination) if isfuture(destination) else None\n\n    def _set_state(future, other):\n        if isfuture(future):\n            _copy_future_state(other, future)\n        else:\n            _set_concurrent_future_state(future, other)\n\n    def _call_check_cancel(destination):\n        if destination.cancelled():\n            if source_loop is None or source_loop is events._get_running_loop():\n                source.cancel()\n            else:\n                source_loop.call_soon_threadsafe(source.cancel)","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/futures.py#L363-L399","documentation":"_chain_future() (used by asyncio.wrap_future, Future chaining, run_coroutine_threadsafe internals) validates that source is an asyncio Future or a concurrent.futures.Future. Passing a coroutine, Task is fine (Task is a Future), but a raw coroutine, None, or another awaitable raises this TypeError.","triggerScenarios":"asyncio.wrap_future(coro) with a coroutine instead of a future; future.add_done_callback chains hand-rolled; calling private _chain_future with a Promise-like object (e.g. a trio future).","commonSituations":"Confusing wrap_future (concurrent -> asyncio bridging) with ensure_future (scheduling); passing the result of an async function instead of a Task; third-party awaitables that duck-type but do not subclass Future.","solutions":["Wrap coroutines first: asyncio.ensure_future(coro) or loop.create_task(coro)","For concurrent.futures futures, use asyncio.wrap_future(cf_future)","If implementing the chaining yourself, check asyncio.isfuture(x) before calling"],"exampleFix":"# before\nasyncio.wrap_future(my_coroutine)  # TypeError: source argument\n\n# after\ntask = asyncio.ensure_future(my_coroutine)\nasyncio.wrap_future(task)  # Task IS a Future","handlingStrategy":"type-guard","validationCode":"import asyncio, concurrent.futures\nok = asyncio.isfuture(src) or isinstance(src, concurrent.futures.Future)","typeGuard":"def is_chainable_source(obj) -> bool:\n    import asyncio, concurrent.futures\n    return asyncio.isfuture(obj) or isinstance(obj, concurrent.futures.Future)","tryCatchPattern":null,"preventionTips":["Convert coroutines with ensure_future()/create_task() before chaining","Remember Task IS a Future; raw coroutines are not","Use public wrap_future() instead of _chain_future()"],"tags":["asyncio","future","typeerror","wrap-future"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}