---
title: "async def Without await — Silent Coroutine Return"
aliases: [missing-await, async-coroutine-silent, await-python-bug]
tags: [python, async, asyncio, fastapi, silent-failure, debugging]
sources:
  - "daily/2026-05-08.md"
created: 2026-05-08
updated: 2026-05-08
---

# async def Without await — Silent Coroutine Return

Python does not raise an error when an `async def` function is called without `await`. It returns a coroutine object instead of the actual result. The only signal is a `RuntimeWarning: coroutine '...' was never awaited` written to stderr — which is easy to miss in production logs.

## The Pattern

```python
# WRONG — get_redis() is async def but called without await
async def check_permission(user_id: str) -> bool:
    redis = get_redis()          # returns coroutine, not connection
    cached = await redis.get(key)  # AttributeError: 'coroutine' object has no attribute 'get'

# CORRECT
async def check_permission(user_id: str) -> bool:
    redis = await get_redis()    # now redis is an actual connection
    cached = await redis.get(key)
```

## Why It's Silent

- Python's `async def` machinery returns a coroutine object immediately when called without `await`
- The coroutine is a valid Python object — it has attributes, can be stored, passed around
- The error only surfaces when you try to use it *as* the expected return type (e.g., calling `.get()` on a coroutine instead of a Redis connection)
- The `RuntimeWarning` is emitted when the garbage collector destroys the unawaited coroutine — potentially seconds later, in a different log line from the original call

## Diagnostic Signals

1. `AttributeError: 'coroutine' object has no attribute '<method>'` — at the point of use
2. `RuntimeWarning: coroutine '<function_name>' was never awaited` — in stderr, may appear later
3. If the coroutine's return value is checked with `if value:` — always truthy (coroutine objects are truthy), so the check passes even though the actual value was never obtained

## Common FastAPI Occurrence

Dependency injection or utility functions that fetch connections:

```python
# Both call sites in authz.py were missing await
async def get_user_permissions(user_id: str):
    redis = get_redis()         # ← bug: missing await
    db = get_database()         # ← bug: missing await
    ...

# Fix
async def get_user_permissions(user_id: str):
    redis = await get_redis()
    db = await get_database()
```

## Detection

```bash
# Grep for async functions called without await
grep -n "= get_redis()\|= get_database()\|= get_session()" backend/
# Any hit that's NOT preceded by "await " is a bug
```

## Related Concepts

- [[wiki/concepts/time-sleep-blocks-asyncio]] — another async gotcha in FastAPI handlers
- [[wiki/concepts/asyncio-contextvar-task-boundary]] — ContextVar propagation across asyncio boundaries

## Sources

- [[daily/2026-05-08.md]] — Discovered in `authz.py` during video-accessibility pipeline debugging; `get_redis()` was async def but called without await at two call sites
