python/cpython · error · RuntimeError
TaskGroup {self!r} is shutting down
Error message
TaskGroup {self!r} is shutting down What it means
RuntimeError raised by TaskGroup.create_task() when the group is aborting (_aborting), i.e. a child task failed and the group is cancelling all siblings during teardown. Accepting new work during shutdown would leak a task no one awaits, so the coroutine is closed and the error raised.
Source
Thrown at Lib/asyncio/taskgroups.py:226
# Suppress any remaining exception (exceptions deserving to be raised
# were raised above).
return True
def create_task(self, coro, **kwargs):
"""Create a new task in this group and return it.
Similar to `asyncio.create_task`.
"""
if not self._entered:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} has not been entered")
if self._exiting and not self._tasks:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} is finished")
if self._aborting:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} is shutting down")
task = self._loop.create_task(coro, **kwargs)
futures.future_add_to_awaited_by(task, self._parent_task)
# Always schedule the done callback even if the task is
# already done (e.g. if the coro was able to complete eagerly),
# otherwise if the task completes with an exception then it will cancel
# the current task too early. gh-128550, gh-128588
self._tasks.add(task)
task.add_done_callback(self._on_task_done)
try:
return task
finally:
# gh-128552: prevent a refcycle of
# task.exception().__traceback__->TaskGroup.create_task->task
del task
# Since Python 3.8 Tasks propagate all exceptions correctly,View on GitHub (pinned to bc6749cc3b)
Solutions
- Handle failures inside task bodies (catch and decide) instead of reacting from outside with more create_task calls
- If work must continue after a failure, run it outside the failed group in a new TaskGroup or as an independent task
- Check group state assumptions: anything that can call create_task after the body's last await must be restructured to run within the body
Example fix
// before
async with asyncio.TaskGroup() as tg:
tg.create_task(worker(queue))
# elsewhere, reacting to a worker error:
tg.create_task(worker(queue)) # group is aborting -> RuntimeError
// after
async def resilient_worker(queue):
while True:
item = await queue.get()
try:
await handle(item)
except Exception:
log.exception('item failed') # keep the group alive
async with asyncio.TaskGroup() as tg:
tg.create_task(resilient_worker(queue)) Defensive patterns
Strategy: validation
Validate before calling
null
Type guard
null
Try / catch
null
Prevention
- Handle per-item failures inside task bodies; do not react to failures by spawning into the aborting group
- Design supervisors so spawning only happens while the body is awaiting, not from external callbacks
- For work surviving a failure, move it to a fresh TaskGroup
When it happens
Trigger: tg.create_task() executed after another child raised an exception (or the group was cancelled) while __aexit__ is cancelling remaining tasks; typical with done-callbacks, external queues draining into create_task, or code racing the abort.
Common situations: Retry/reschedule logic triggered by a failing task that tries to re-enqueue work into the now-aborting group; a supervisor loop polling 'should I spawn more' that wins the race against an error-triggered shutdown; multiplexed workers fed from an external producer.
Related errors
- TaskGroup {self!r} has already been entered
- unhandled errors in a TaskGroup
- TaskGroup {self!r} has not been entered
- TaskGroup {self!r} is finished
- Transport is closing
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/d9e86cfea78511cb.
Report an issue: GitHub.