This rule raises an issue when asyncio.create_task() or asyncio.ensure_future() is called without saving the returned task.

Why is this an issue?

When creating asyncio tasks with asyncio.create_task() or asyncio.ensure_future(), you create independent units of work that execute concurrently. However, if you don’t save the returned task object in a variable or collection, the task may be garbage collected at any time, even before it’s done.

This happens because the event loop only maintains a weak reference to tasks. Without a strong reference:

For a task to run to completion and handle exceptions properly, you must save the task reference and eventually await it.

How to fix it

To properly handle asyncio tasks:

Code examples

Noncompliant code example

import asyncio

async def fetch_data():
    asyncio.create_task(process_data()) # Noncompliant: task may be garbage collected before completion

async def process_data():
    await asyncio.sleep(1)
    return {"result": "processed"}

Compliant solution

import asyncio

async def fetch_data():
    task = asyncio.create_task(process_data()) # Compliant
    await task

async def process_data():
    await asyncio.sleep(1)
    return {"result": "processed"}

Or, using TaskGroup (Python 3.11+):

import asyncio

async def fetch_data():
    async with asyncio.TaskGroup() as tg:
        # Tasks are managed by the TaskGroup and won't be garbage collected
        tg.create_task(process_data())

async def process_data():
    await asyncio.sleep(1)
    return {"result": "processed"}

Resources

Documentation