{"record":{"id":"73396fd3b1ffbd92","repo":"python/cpython","slug":"a-future-is-required-for-destination-argument","errorCode":null,"errorMessage":"A future is required for destination argument","messagePattern":"A future is required for destination argument","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/futures.py","lineNumber":384,"sourceCode":"        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)\n\n    def _call_set_state(source):\n        if (destination.cancelled() and","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/futures.py#L366-L402","documentation":"The destination-side validation of _chain_future(): the object that will receive the source's result must be an asyncio.Future or concurrent.futures.Future. Because chaining copies state into destination, a non-future destination has no set_result/set_exception API to copy into.","triggerScenarios":"asyncio.futures._chain_future(task, some_coroutine); wrap_future implemented by hand with the arguments swapped; passing a list/None/callback as destination in custom bridging code.","commonSituations":"Argument order confusion in private API use; building a concurrent.futures bridge where the destination executor future was never created (submit() result forgotten); tests passing Mock objects.","solutions":["Create the destination future first (loop.create_future() or executor.submit(...))","Prefer public APIs: asyncio.wrap_future(source) already builds a correct destination","Double-check argument order: _chain_future(source, destination)"],"exampleFix":"# before\nasyncio.futures._chain_future(task, result_callback)  # callback is not a future\n\n# after\ndest = loop.create_future()\ndest.add_done_callback(result_callback)\nasyncio.futures._chain_future(task, dest)","handlingStrategy":"type-guard","validationCode":"import asyncio, concurrent.futures\nok = asyncio.isfuture(dst) or isinstance(dst, concurrent.futures.Future)","typeGuard":"def is_chainable_destination(obj) -> bool:\n    import asyncio, concurrent.futures\n    return asyncio.isfuture(obj) or isinstance(obj, concurrent.futures.Future)","tryCatchPattern":null,"preventionTips":["Create the destination future before chaining","Prefer wrap_future(), which builds the destination for you","Keep argument order straight: _chain_future(source, destination)"],"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"}