python/cpython · error · RuntimeError
TaskGroup {self!r} cannot determine the parent task
Error message
TaskGroup {self!r} cannot determine the parent task What it means
RuntimeError raised by TaskGroup.__aenter__ when tasks.current_task() returns None, i.e. the code entering the group is not running inside a Task. TaskGroup relies on a parent task to attach cancellation semantics (child failures cancel the parent), so entering it from bare callback/loop context is unsupported.
Source
Thrown at Lib/asyncio/taskgroups.py:64
if self._errors:
info.append(f'errors={len(self._errors)}')
if self._aborting:
info.append('cancelling')
elif self._entered:
info.append('entered')
info_str = ' '.join(info)
return f'<TaskGroup{info_str}>'
async def __aenter__(self):
if self._entered:
raise RuntimeError(
f"TaskGroup {self!r} has already been entered")
if self._loop is None:
self._loop = events.get_running_loop()
self._parent_task = tasks.current_task(self._loop)
if self._parent_task is None:
raise RuntimeError(
f'TaskGroup {self!r} cannot determine the parent task')
self._entered = True
if self._cancel_on_enter:
self.cancel()
return self
async def __aexit__(self, et, exc, tb):
tb = None
try:
return await self._aexit(et, exc)
finally:
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them. It would be nicer to use a try/finally
# in __aexit__ directly but that introduced some diff noise
self._parent_task = None
self._errors = NoneView on GitHub (pinned to bc6749cc3b)
Solutions
- Ensure 'async with TaskGroup()' appears inside a coroutine that is scheduled as a Task (asyncio.run, loop.create_task, asyncio.create_task)
- Move TaskGroup usage out of callbacks: wrap the whole workflow in one top-level coroutine task
- For async generators needing structured concurrency, restructure so the group is entered and exited within a live task, not during aclose() from shutdown
Example fix
// before
async def worker():
async with asyncio.TaskGroup() as tg: ...
loop.call_soon(lambda: asyncio.ensure_future(worker())) # misdriven contexts can lack a current task
// after
async def worker():
async with asyncio.TaskGroup() as tg: ...
asyncio.run(worker()) # always runs inside a Task Defensive patterns
Strategy: validation
Validate before calling
null
Type guard
null
Try / catch
null
Prevention
- Enter TaskGroups only inside coroutines scheduled via asyncio.run/create_task
- Never drive the entering code with manual send()/throw() or from loop callbacks
- Keep structured concurrency out of async-generator finalization paths
When it happens
Trigger: Awaiting tg.__aenter__ from code driven outside a Task: e.g. directly inside a loop.run_until_complete chain is fine (it wraps in a Task) but code executed via loop.call_soon callbacks, signal handlers, or low-level manual coroutine stepping has no current task; also entering a TaskGroup during interpreter/loop shutdown when the task context is already gone.
Common situations: Async generators asynchat-style finalized via loop.shutdown_asyncgens after their task died; driving coroutines manually with coro.send(None) in tests; exotic embedding of asyncio in another runtime; calling __aenter__ from a thread other than the loop thread.
Related errors
- TaskGroup {self!r} has already been entered
- TaskGroup {self!r} has not been entered
- {htmldir!r} is not a Sphinx HTML output directory
- {name} is not part of stable ABI. Document it as `c:macro::`
- deprecated-removed:: second argument cannot be `next`
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/d8001787803be8f4.
Report an issue: GitHub.