Missing Await on Coroutine

ID

python.missing_await_coroutine

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Python

Tags

reliability, suspicious-call

Description

Reports a call to an async def function whose returned coroutine is consumed without await. The call returns a coroutine object, not the resolved value, so the caller almost always gets a surprising result and Python emits RuntimeWarning: coroutine 'foo' was never awaited when the coroutine is garbage-collected.

async def fetch(url):
    return url

async def caller():
    fetch("/x")            # FLAW — coroutine never awaited
    value = fetch("/y")    # FLAW — value is a coroutine object, not the resolved value
    await fetch("/z")      # OK

The rule resolves the callee via the local symbol table — only bare-name calls to in-module async def functions are detected. Calls intentionally scheduled via asyncio.create_task / ensure_future are out of scope (they return a Task, not a coroutine).

Rationale

A dangling coroutine almost never does what the writer intended: comparisons fail, str() returns <coroutine object …>, and the underlying I/O never runs.

Remediation

  • Add await if you want the resolved value.

  • Use asyncio.create_task(…​) if you want the call to run concurrently and you’ll await it later.

  • If you’re inside a sync function, call from an event loop (asyncio.run(…​)) — synchronous code cannot await directly.