agentsclimarketplace

Async fn called without await

Skill viditkbhatnagar/immunize/src/immunize/patterns/async-fn-called-without-await

A curated pattern library that stops AI coding assistants from repeating common runtime errors. No API key. No LLM calls at runtime.

Install
npx -y skills add viditkbhatnagar/immunize --skill async-fn-called-without-await

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when calling an async def function from Python to ensure every call is awaited before its return value is used as the resolved result.

SKILL.md

2.0 KB, as published. Nobody here has run it

async-fn-called-without-await

Every async def call returns a coroutine object, not the value the body produces. Using that coroutine as if it were the resolved value raises a TypeError at runtime, and Python also emits a warning when the unused coroutine is garbage-collected:

TypeError: unsupported operand type(s) for *: 'coroutine' and 'int'
RuntimeWarning: coroutine 'fetch_value' was never awaited

Both signals point at the same bug: a missing await.

Example

Wrong — value is a coroutine, not an int:

async def fetch_value() -> int:
    return 21

async def compute_total() -> int:
    value = fetch_value()          # missing await
    return value * 2

Right — await resolves the coroutine to its return value:

async def fetch_value() -> int:
    return 21

async def compute_total() -> int:
    value = await fetch_value()
    return value * 2

At the sync boundary, use asyncio.run

result = asyncio.run(compute_total())

Inside sync code, asyncio.run() drives a single coroutine to completion and returns its value. Inside async code, always await; never call asyncio.run() from an already-running loop.

Running many in parallel

Launch with asyncio.gather — a plain list comprehension produces a list of un-awaited coroutines:

users = await asyncio.gather(*(fetch_user(i) for i in ids))

Catch this earlier

Set PYTHONASYNCIODEBUG=1 in development. It surfaces un-awaited coroutines as warnings the moment they are garbage-collected, instead of waiting for a downstream TypeError. Static type checkers (mypy, pyright) also flag this when return annotations are present — assigning a coroutine to an int-annotated variable is a type error they catch before the code ever runs.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.