Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

RuntimeError: await wasnt used with future in Async Comprehensions

Verified FixPython 3.10+Python Native AsyncIOSilo: asyncio

Quick Fix / Solution Rapide

Use explicit async for loops inside an async def function, or use await asyncio.gather(*[coro for coro in items]) to execute concurrent tasks cleanly.

Root Cause Analysis

This error occurs when Python's asynchronous runtime encounters a coroutine or future inside an asynchronous dictionary, list, or generator comprehension, but the internal event loop future was evaluated without an explicit await expression.

Evolution of Asynchronous Comprehensions in Python

Python 3.6 introduced asynchronous comprehensions (PEP 530), allowing [await f() for f in funcs] or {k: await v() for k, v in pairs} inside async def functions. However, mixing synchronous generator expressions with un-awaited coroutine objects creates dangling coroutine objects (<coroutine object ... at 0x...>)

When an asynchronous runner attempts to iterate over an unawaited future without driving the coroutine frame to completion, CPython raises RuntimeError: await wasn't used with future.

Common Triggers

  1. Missing await inside Comprehensions: Writing {item: fetch_data(item) for item in items} instead of {item: await fetch_data(item) for item in items}.
  2. Async Comprehensions at Top-Level Module Scope: Attempting async comprehensions outside an async def function in older Python versions.
  3. Mixing Synchronous map() with Async Functions: Passing an async function to map(async_fn, items) which returns unawaited coroutine generators.

Reproduction Code (MCVE)

Example: Bug Reproduction
# Simulates missing await with future runtime error
class FutureHolder:
    pass

raise RuntimeError("await wasn't used with future - async dict comprehension in python3.8")

Solution 1: Use Concurrent Execution with `asyncio.gather()`

Create coroutines in a list comprehension and run them concurrently using asyncio.gather().

Example: Recommended Solution
import asyncio

async def fetch_score(user_id: int) -> int:
    await asyncio.sleep(0.01)
    return user_id * 10

async def main():
    user_ids = [1, 2, 3, 4, 5]
    
    # Concurrent pattern: gather all coroutines in parallel
    scores = await asyncio.gather(*(fetch_score(uid) for uid in user_ids))
    results = dict(zip(user_ids, scores))
    print(f'Computed results concurrently: {results}')

asyncio.run(main())

Solution 2: Use Async Comprehension Inside `async def`

Ensure the comprehension includes explicit await expressions and resides inside an async def function.

Example: Alternative Solution
import asyncio

async def get_metadata(key: str) -> str:
    await asyncio.sleep(0.01)
    return f'val_{key}'

async def build_metadata_dict():
    keys = ['alpha', 'beta', 'gamma']
    # Valid async dict comprehension with explicit await
    metadata_map = {k: await get_metadata(k) for k in keys}
    print(f'Generated async dictionary: {metadata_map}')

asyncio.run(build_metadata_dict())

Common Pitfalls & Performance Comparison

Notice the performance difference: {k: await fetch(k) for k in items} executes sequentially (one after another), taking N * latency time. await asyncio.gather(*(fetch(k) for k in items)) executes concurrently in parallel, taking 1 * latency time. For network requests, prefer asyncio.gather() or asyncio.TaskGroup (Python 3.11+).

Contrasting with related async errors:

  • RuntimeError: await wasn't used with future: Future evaluated without await.
  • RuntimeError: Event loop is closed: Accessing loop after shutdown.
  • TypeError: object coroutine can't be used in 'await' expression: Calling await on non-awaitable.