python/cpython · error · BaseExceptionGroup
unhandled errors in a TaskGroup
Error message
unhandled errors in a TaskGroup
What it means
BaseExceptionGroup('unhandled errors in a TaskGroup') is raised from TaskGroup.__aexit__ when one or more child tasks finished with exceptions that nothing else re-raised. It bundles every child failure (plus any interaction with an in-flight exception 'et'); a special case re-raises a sole child exception during GeneratorExit so async generators close cleanly.
Source
Thrown at Lib/asyncio/taskgroups.py:202
# which will keep the cancel count stable.
if self._parent_task.cancelling():
self._parent_task.uncancel()
self._parent_task.cancel()
try:
# If the *only* error is a GeneratorExit from the body
# of the group, then instead of raising an
# ExceptionGroup we raise GeneratorExit. This ensures
# that async generators that use TaskGroup properly
# swallow the exception on `aclose()` while ensuring
# that no exceptions from subtasks are swallowed.
if (
et is not None
and issubclass(et, GeneratorExit)
and len(self._errors) == 1
):
raise exc
else:
raise BaseExceptionGroup(
'unhandled errors in a TaskGroup',
self._errors,
) from None
finally:
exc = None
# 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")View on GitHub (pinned to bc6749cc3b)
Solutions
- Catch with except* (ExceptionGroup handling): 'except* ValueError as eg:' to handle each child failure type
- Add try/except inside task bodies for expected per-task errors so the group only propagates truly fatal ones
- Inspect eg.exceptions recursively to find root causes; log them before deciding on restart/abort
Example fix
// before
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch(url))
// after
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch(url))
except* ValueError as eg:
for exc in eg.exceptions:
log.warning('fetch failed: %r', exc) Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(fetch(u)) for u in urls]
except* http.HTTPError as eg:
log.warning('http failures: %r', eg.exceptions)
except* OSError as eg:
raise # fatal, abort the batch Prevention
- Catch expected per-task failures inside the task body so the group only sees fatal errors
- Use except* (not except ExceptionGroup) for typed handling of child failures
- Log eg.exceptions recursively before deciding restart policy
When it happens
Trigger: One or more tasks created via tg.create_task() raise before the 'async with' block exits; also combined failures during group abort, where sibling cancellations and the original error are collected together.
Common situations: Structured-concurrency fan-out where one worker raises (network error, bug) and you did not catch it inside the task body; several workers failing simultaneously (e.g. shared dependency down) producing a multi-exception group; nested TaskGroups producing nested groups.
Related errors
- TaskGroup {self!r} has already been entered
- TaskGroup {self!r} has not been entered
- TaskGroup {self!r} is finished
- TaskGroup {self!r} is shutting down
- create_connection failed
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/94136c6b4d5cc5ad.
Report an issue: GitHub.