python/cpython · error · TypeError
A future is required for destination argument
Error message
A future is required for destination argument
What it means
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.
Source
Thrown at Lib/asyncio/futures.py:384
dest.cancel()
elif exception is not None:
dest.set_exception(_convert_future_exc(exception))
else:
dest.set_result(result)
def _chain_future(source, destination):
"""Chain two futures so that when one completes, so does the other.
The result (or exception) of source will be copied to destination.
If destination is cancelled, source gets cancelled too.
Compatible with both asyncio.Future and concurrent.futures.Future.
"""
if not isfuture(source) and not isinstance(source,
concurrent.futures.Future):
raise TypeError('A future is required for source argument')
if not isfuture(destination) and not isinstance(destination,
concurrent.futures.Future):
raise TypeError('A future is required for destination argument')
source_loop = _get_loop(source) if isfuture(source) else None
dest_loop = _get_loop(destination) if isfuture(destination) else None
def _set_state(future, other):
if isfuture(future):
_copy_future_state(other, future)
else:
_set_concurrent_future_state(future, other)
def _call_check_cancel(destination):
if destination.cancelled():
if source_loop is None or source_loop is events._get_running_loop():
source.cancel()
else:
source_loop.call_soon_threadsafe(source.cancel)
def _call_set_state(source):
if (destination.cancelled() andView on GitHub (pinned to bc6749cc3b)
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)
Example fix
# before asyncio.futures._chain_future(task, result_callback) # callback is not a future # after dest = loop.create_future() dest.add_done_callback(result_callback) asyncio.futures._chain_future(task, dest)
Defensive patterns
Strategy: type-guard
Validate before calling
import asyncio, concurrent.futures ok = asyncio.isfuture(dst) or isinstance(dst, concurrent.futures.Future)
Type guard
def is_chainable_destination(obj) -> bool:
import asyncio, concurrent.futures
return asyncio.isfuture(obj) or isinstance(obj, concurrent.futures.Future) Prevention
- Create the destination future before chaining
- Prefer wrap_future(), which builds the destination for you
- Keep argument order straight: _chain_future(source, destination)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A future is required for source argument
- {future!r} object does not appear to be compatible with asyn
- Not implemented
- Unknown thread safety level {level!r} for {name!r}. Valid le
- Socket cannot be of type SSLSocket
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/73396fd3b1ffbd92.
Report an issue: GitHub.