Timeouts and cancellation in asyncio prevent a slow operation from holding an application forever. Cancellation is cooperative: the task receives CancelledError at its next suspension point and can release resources. Code must respect that signal so structured concurrency keeps working.
Start with the Python async and await guide if coroutine basics are unfamiliar. For related child tasks, see asyncio.TaskGroup.
Bound a block with asyncio.timeout
Python 3.11 and later provide asyncio.timeout() for applying one deadline to several operations:
import asyncio
async def load_page():
try:
async with asyncio.timeout(2.5):
connection = await open_connection()
return await connection.receive()
except TimeoutError:
return None
Catch TimeoutError outside the block. Internally, the context manager cancels the current task and converts CancelledError as it exits. For an absolute deadline, calculate against the event loop's monotonic clock and use asyncio.timeout_at().
wait_for limits one awaitable
result = await asyncio.wait_for(call_api(), timeout=2)
When time expires, wait_for cancels the awaitable and waits for cancellation to finish. Actual elapsed time may exceed two seconds if cleanup is slow. In contrast, asyncio.wait returns sets of done and pending tasks without automatically cancelling pending work.
Always release resources in finally
async def consume(queue):
resource = await open_resource()
try:
while True:
item = await queue.get()
await process(item)
queue.task_done()
finally:
await resource.close()
If you catch asyncio.CancelledError to log or clean up, use raise afterward. Swallowing it can make TaskGroup and asyncio.timeout() misbehave.
async def worker():
try:
await run()
except asyncio.CancelledError:
logger.info("worker cancelled")
raise
Cancel and await the task
A cancellation request does not mean the task has already stopped:
task = asyncio.create_task(worker())
try:
await use_task(task)
finally:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
Awaiting gives inner finally blocks a chance to run and avoids orphaned tasks. Services should also put a deadline around shutdown and report cleanup that does not finish.
Use shield sparingly
asyncio.shield() prevents caller cancellation from propagating to an inner task, but the caller still receives CancelledError. It can be appropriate for completing a critical write that has already started. Keep a strong reference to the task and decide who observes its result or exception.
Do not use shield to hide an architecture with no bounds. External operations need their own timeouts, and shutdown needs an overall budget.
Test slow paths
Test normal completion, timeout, external cancellation, and errors during cleanup. Verify that connections, locks, and queue items are released. Use controlled synchronization in tests instead of long sleeps that make the suite flaky.
The official asyncio tasks and timeouts documentation, accessed July 28, 2026, explains conversion to TimeoutError, cancellation propagation, and the limits of shield.