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 task

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Move every create_task call inside the 'async with asyncio.TaskGroup() as tg:' block
  2. If you must accept work before entering, buffer coroutines in a list and spawn them right after __aenter__
  3. 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

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


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/8b2459ba0917afaa. Report an issue: GitHub.