python/cpython · error · RuntimeError
TaskGroup {self!r} has not been entered
Error message
TaskGroup {self!r} has not been entered What it means
RuntimeError raised by TaskGroup.create_task() when called before __aenter__ has run (_entered is false). Until the group is entered it has no parent task or lifecycle to attach the new task to, so the passed coroutine is closed (to avoid a 'never awaited' warning) and the error is raised.
Source
Thrown at Lib/asyncio/taskgroups.py:220
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")
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 taskView on GitHub (pinned to bc6749cc3b)
Solutions
- Move every create_task call inside the 'async with asyncio.TaskGroup() as tg:' block
- If you must accept work before entering, buffer coroutines in a list and spawn them right after __aenter__
- Note the coroutine passed is closed by the failed call — build fresh coroutines when retrying
Example fix
// before
tg = asyncio.TaskGroup()
tg.create_task(poll()) # RuntimeError
async with tg: ...
// after
async with asyncio.TaskGroup() as tg:
tg.create_task(poll()) Defensive patterns
Strategy: validation
Validate before calling
null
Type guard
null
Try / catch
null
Prevention
- Keep every tg.create_task lexically inside the 'async with TaskGroup()' block
- Buffer pre-arrival work in a list and spawn it right after entering the group
- Note the failed call closes the coroutine — always build a fresh one on retry
When it happens
Trigger: tg = asyncio.TaskGroup(); tg.create_task(coro) without an enclosing 'async with tg:'; exposing create_task in a class method while the group is entered elsewhere/later; storing tasks at __init__ time before the context starts.
Common situations: Pre-registering startup work on a group before entering it; copy-pasting code from inside the block to outside; mixing up TaskGroup with the raw loop.create_task API where no entering is needed.
Related errors
- TaskGroup {self!r} has already been entered
- TaskGroup {self!r} cannot determine the parent task
- unhandled errors in a TaskGroup
- TaskGroup {self!r} is finished
- TaskGroup {self!r} is shutting down
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/8b2459ba0917afaa.
Report an issue: GitHub.