RustPython/RustPython · error · TypeError

A future is required for destination argument

Error message

A future is required for destination argument

What it means

The destination-side twin of the source check in _chain_future(): whatever receives the copied state must also be an asyncio.Future or concurrent.futures.Future, otherwise TypeError('A future is required for destination argument'). The destination is usually produced by your own code (loop.create_future() or concurrent.futures.Future()), so this error almost always means a coroutine, awaitable, or unrelated object was passed where a fresh future was expected.

Source

Thrown at Lib/asyncio/futures.py:383

            dest.set_exception(_convert_future_exc(exception))
        else:
            result = source.result()
            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() and

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Create the destination properly: dest = loop.create_future() (asyncio side) or concurrent.futures.Future() (thread side)
  2. Prefer the public helpers — asyncio.run_coroutine_threadsafe() and wrap_future() — instead of _chain_future
  3. Validate the destination with asyncio.isfuture()/isinstance checks before chaining

Example fix

# before
asyncio.futures._chain_future(src_fut, dest_coroutine)  # TypeError

# after
dest = loop.create_future()
asyncio.futures._chain_future(src_fut, dest)
Defensive patterns

Strategy: type-guard

Validate before calling

def make_destination(loop, thread_side=False):
    import concurrent.futures
    return concurrent.futures.Future() if thread_side else loop.create_future()

Type guard

def is_valid_destination(obj) -> bool:
    import concurrent.futures
    return asyncio.isfuture(obj) or isinstance(obj, concurrent.futures.Future)

Prevention

When it happens

Trigger: Calling the private _chain_future(source, dest) with dest as a coroutine or custom awaitable; passing a Task-like object lacking the Future interface; reusing old snippets that chained into a result container instead of a real future.

Common situations: Hand-rolled bridging between asyncio and thread pools; test code passing mocks as destinations; adapting _chain_future examples from outdated answers.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/3ff968d055e27951. Report an issue: GitHub.