Async Python Cancellation and Timeouts: Stop Work Without Leaking It
Understand task cancellation, timeout scopes, cleanup, and the failure modes that leave background work running.
On this page
Cancellation is a request to stop
asyncio cancellation is delivered to a task as CancelledError at an await point. It gives the coroutine a chance to unwind and release resources; it is not a guarantee that arbitrary synchronous work is interrupted. A CPU-bound loop that never yields can prevent both cancellation and unrelated tasks from running.
Use try/finally or async context managers for cleanup. If a coroutine catches cancellation to perform cleanup, it should normally re-raise it afterward. Swallowing cancellation can make a timeout appear to finish while its underlying work continues.
async def read_and_store(client, key):
resource = await client.open(key)
try:
return await resource.read()
finally:
await resource.close()
Put timeouts around the operation that owns the budget
An end-to-end request has a deadline. Each dependency should receive the remaining budget rather than an unrelated full timeout. A three-second HTTP timeout inside a two-second request deadline is inconsistent; cancellation may arrive first, and cleanup still needs time.
Python versions provide timeout scopes such as asyncio.timeout(); older code may use asyncio.wait_for(). Understand which task is cancelled and whether the underlying operation cooperates. If a library ignores cancellation or delegates to a thread, the work may continue after the caller times out.
Task ownership and background work
Create tasks through a structured owner that awaits or cancels them. Fire-and-forget tasks can lose exceptions, outlive the request, or be cancelled abruptly at shutdown. If work must survive process restarts, put it in a durable queue rather than relying on an in-memory task.
When multiple tasks are cancelled, gather their results or exceptions deliberately. Do not assume task-group behavior and cancellation propagation are identical across all Python versions and APIs.
Diagnose stuck tasks
Record task names and operation identifiers. Inspect pending task stacks during an incident, check whether tasks are blocked on a semaphore or pool, and measure cancellation-to-cleanup time. A timeout counter without the duration of abandoned work can hide a growing backlog.
Fix checklist
- Find blocking synchronous calls that prevent the event loop from yielding.
- Bound connection pools, semaphores, and queues.
- Propagate the remaining deadline to each layer.
- Release resources in
finallyand re-raise cancellation. - Move durable or long-running work to a supervised worker system.
Timeouts improve responsiveness only when the work they abandon is stopped or safely contained.