Skip to content

asyncio.TaskGroup's uncancelation behavior in 3.12 is inconsisted with others (3.1x) implementations #133747

@realitycheck

Description

@realitycheck

Bug report

Bug description:

In python3.12 during asyncio.wait_for timeout there is an unexpected propagation of asyncio.CancelledError
to outer scope when an exception raised inside an inner asyncio.TaskGroup has been delayed by parent coroutine.

The example below represents an inconsistent behavior of py3.12 asyncio.TaskGroup:

import asyncio

async def delay(callable):
    try:
        await callable()
    except Exception:
        await asyncio.sleep(1)
        raise


async def raise_error_in_task_group():
    async def fail():
        raise Exception()

    async with asyncio.TaskGroup() as tg:
        tg.create_task(fail())


try:
    asyncio.run(
        asyncio.wait_for(
            delay(raise_error_in_task_group), timeout=0.1
        )
    )
except asyncio.TimeoutError:
    print("NO ISSUE")

To reproduce:

  1. Save above code as issue.py
  2. Run via docker run --rm -v$(pwd):/app:ro -w /app python:3.12 python issue.py

Below is the produced output by various python3.1x versions. Notice that only 3.12 has unique (unexpected) behavior.

▶ docker run --rm -v$(pwd):/app:ro -w /app python:3.10 python issue.py
NO ISSUE

▶ docker run --rm -v$(pwd):/app:ro -w /app python:3.11 python issue.py
NO ISSUE

▶ docker run --rm -v$(pwd):/app:ro -w /app python:3.13 python issue.py
NO ISSUE

▶ docker run --rm -v$(pwd):/app:ro -w /app python:3.12 python issue.py
  + Exception Group Traceback (most recent call last):
  |   File "/app/issue.py", line 65, in delay
  |     await callable()
  |   File "/app/issue.py", line 75, in raise_error_in_task_group
  |     async with asyncio.TaskGroup() as tg:
  |                ^^^^^^^^^^^^^^^^^^^
  |   File "/usr/local/lib/python3.12/asyncio/taskgroups.py", line 71, in __aexit__
  |     return await self._aexit(et, exc)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/usr/local/lib/python3.12/asyncio/taskgroups.py", line 164, in _aexit
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/app/issue.py", line 73, in fail
    |     raise Exception()
    | Exception
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/app/issue.py", line 80, in <module>
    asyncio.run(
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 195, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/tasks.py", line 520, in wait_for
    return await fut
           ^^^^^^^^^
  File "/app/issue.py", line 67, in delay
    await asyncio.sleep(1)
  File "/usr/local/lib/python3.12/asyncio/tasks.py", line 665, in sleep
    return await future
           ^^^^^^^^^^^^
asyncio.exceptions.CancelledError

▶ docker run --rm -v$(pwd):/app:ro -w /app python:3.12 python -VV
Python 3.12.10 (main, Apr 29 2025, 00:24:54) [GCC 12.2.0]

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixesstdlibStandard Library Python modules in the Lib/ directorytopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions