python.asyncio.missing_timeout
Stability
Medium
Detects async operations that should have timeouts but don’t, which can lead to tasks hanging indefinitely.
Why It Matters
Section titled “Why It Matters”Async operations without timeouts can:
- Hang indefinitely — A single stuck task can hold resources forever
- Exhaust connection pools — Tasks waiting on unresponsive services never release connections
- Cascade failures — One slow operation can block entire request pipelines
- Prevent graceful shutdown — Untimed operations ignore cancellation signals
Example
Section titled “Example”# ❌ Before (can wait forever)import asyncio
async def main(): done, pending = await asyncio.wait(tasks)
async def get_from_queue(queue): item = await queue.get() # Blocks forever if empty# ✅ After (bounded waiting)import asyncio
async def main(): done, pending = await asyncio.wait(tasks, timeout=30.0) # Handle pending tasks for task in pending: task.cancel()
async def get_from_queue(queue): try: item = await asyncio.wait_for(queue.get(), timeout=30.0) except asyncio.TimeoutError: # Handle timeout passWhat Unfault Detects
Section titled “What Unfault Detects”asyncio.wait()withouttimeoutparameterasyncio.gather()without timeout wrapperQueue.get()without timeoutLock.acquire()without timeout- Very long
asyncio.sleep()calls without cancellation support
Auto-Fix
Section titled “Auto-Fix”Unfault generates patches that add appropriate timeout parameters to async operations:
# asyncio.wait with timeoutdone, pending = await asyncio.wait(tasks, timeout=30.0)
# asyncio.gather with timeout wrapper (Python 3.11+)async with asyncio.timeout(30.0): results = await asyncio.gather(task1, task2, task3)