Skip to content
Sarj Code Standards

cancel-owned-task-without-join

Join a cancelled locally owned task before releasing its lifecycle.

Why

Task.cancel requests cancellation without waiting for the child to finish; an async lifespan can release resources while its child still runs cancellation cleanup.

Fix

Await the owned task or an appropriate gather/TaskGroup settlement before releasing resources. Preserve cancellation of the owner and choose an explicit policy for unexpected child errors and repeated cancellation.

Examples

Before — flagged Cancelling does not wait for the owned child
app/lifecycle.py
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan():
task = asyncio.create_task(work())
try:
yield
finally:
task.cancel()
After — preferred Wait for the child before the lifespan returns
app/lifecycle.py
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan():
task = asyncio.create_task(work())
try:
yield
finally:
task.cancel()
await asyncio.gather(task, return_exceptions=True)