RustPython/RustPython · error · TypeError

An asyncio.Future, a coroutine or an awaitable is required

Error message

An asyncio.Future, a coroutine or an awaitable is required

What it means

Runner.run() accepts a coroutine, or any awaitable (futures, objects with __await__) which it wraps transparently via _wrap_awaitable. Passing anything else — an int, str, list, an already-computed result, or a coroutine function that was never called — raises TypeError('An asyncio.Future, a coroutine or an awaitable is required'). This mirrors asyncio.run()'s contract for its main argument.

Source

Thrown at Lib/asyncio/runners.py:104

        return self._loop

    def run(self, coro, *, context=None):
        """Run code in the embedded event loop."""
        if events._get_running_loop() is not None:
            # fail fast with short traceback
            raise RuntimeError(
                "Runner.run() cannot be called from a running event loop")

        self._lazy_init()

        if not coroutines.iscoroutine(coro):
            if inspect.isawaitable(coro):
                async def _wrap_awaitable(awaitable):
                    return await awaitable

                coro = _wrap_awaitable(coro)
            else:
                raise TypeError('An asyncio.Future, a coroutine or an '
                                'awaitable is required')

        if context is None:
            context = self._context

        task = self._loop.create_task(coro, context=context)

        if (threading.current_thread() is threading.main_thread()
            and signal.getsignal(signal.SIGINT) is signal.default_int_handler
        ):
            sigint_handler = functools.partial(self._on_sigint, main_task=task)
            try:
                signal.signal(signal.SIGINT, sigint_handler)
            except ValueError:
                # `signal.signal` may throw if `threading.main_thread` does
                # not support signals (e.g. embedded interpreter with signals
                # not registered - see gh-91880)
                sigint_handler = None

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Pass the coroutine object: runner.run(main(args)) — note the parentheses
  2. Pre-check arbitrary work with inspect.iscoroutine(work) or inspect.isawaitable(work) before calling run()
  3. Wrap plain values in an async function: runner.run(process(value))

Example fix

# before
runner.run(main)   # function object, not a coroutine -> TypeError

# after
runner.run(main()) # coroutine object
Defensive patterns

Strategy: type-guard

Validate before calling

import inspect

def as_awaitable(work):
    if inspect.iscoroutine(work) or inspect.isawaitable(work):
        return work
    raise TypeError(
        f"Runner.run needs a coroutine or awaitable, got {type(work).__name__}")

runner.run(as_awaitable(work))

Type guard

import inspect

def is_awaitable(obj) -> bool:
    return inspect.iscoroutine(obj) or inspect.isawaitable(obj)

Prevention

When it happens

Trigger: runner.run(42) or runner.run('text'); runner.run(main) (function object instead of main()); passing the return value of a synchronous client call; generic driver code receiving 'work' of unknown type.

Common situations: Forgetting the call parentheses on the coroutine function; passing results of sync libraries (requests, database drivers) into the Runner; wrappers that accept either a value or a coroutine without distinguishing them.

Related errors


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