{"record":{"id":"429abe36d77e14b4","repo":"python/cpython","slug":"taskgroup-self-r-is-finished","errorCode":null,"errorMessage":"TaskGroup {self!r} is finished","messagePattern":"TaskGroup (.+?) is finished","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/taskgroups.py","lineNumber":223,"sourceCode":"                    ) from None\n            finally:\n                exc = None\n\n        # Suppress any remaining exception (exceptions deserving to be raised\n        # were raised above).\n        return True\n\n    def create_task(self, coro, **kwargs):\n        \"\"\"Create a new task in this group and return it.\n\n        Similar to `asyncio.create_task`.\n        \"\"\"\n        if not self._entered:\n            coro.close()\n            raise RuntimeError(f\"TaskGroup {self!r} has not been entered\")\n        if self._exiting and not self._tasks:\n            coro.close()\n            raise RuntimeError(f\"TaskGroup {self!r} is finished\")\n        if self._aborting:\n            coro.close()\n            raise RuntimeError(f\"TaskGroup {self!r} is shutting down\")\n        task = self._loop.create_task(coro, **kwargs)\n\n        futures.future_add_to_awaited_by(task, self._parent_task)\n\n        # Always schedule the done callback even if the task is\n        # already done (e.g. if the coro was able to complete eagerly),\n        # otherwise if the task completes with an exception then it will cancel\n        # the current task too early. gh-128550, gh-128588\n        self._tasks.add(task)\n        task.add_done_callback(self._on_task_done)\n        try:\n            return task\n        finally:\n            # gh-128552: prevent a refcycle of\n            # task.exception().__traceback__->TaskGroup.create_task->task","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/taskgroups.py#L205-L241","documentation":"RuntimeError raised by TaskGroup.create_task() when the group is exiting with no remaining tasks (_exiting and not self._tasks): the body has finished and __aexit__ is winding down, so new work cannot join. The passed coroutine is closed before raising.","triggerScenarios":"A task-finished callback or scheduled call that invokes tg.create_task() while the 'async with' body is completing; e.g. calling create_task from on_done callbacks, from a sibling task's finally block racing the exit, or from code woken by the last task completing.","commonSituations":"Chained/continuation spawning inside the group ('when X finishes, spawn Y') without awaiting Y within the body; fire-and-forget helpers that lazily create tasks and get invoked at teardown; races between a worker finishing and a supervisor trying to enqueue a replacement at shutdown.","solutions":["Keep spawn logic inside the live body of the 'async with' block and await completion there","For fire-and-forget work that must outlive the group, use asyncio.create_task on the loop (and track it separately) rather than the dying group","Restructure continuation patterns: the task that finishes should itself schedule its successor before returning, while the group is still active"],"exampleFix":"// before\nasync with asyncio.TaskGroup() as tg:\n    t = tg.create_task(job())\n    t.add_done_callback(lambda _: tg.create_task(cleanup()))  # may fire during exit -> RuntimeError\n\n// after\nasync def job_then_cleanup():\n    await job()\n    await cleanup()\nasync with asyncio.TaskGroup() as tg:\n    tg.create_task(job_then_cleanup())","handlingStrategy":"validation","validationCode":"null","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Spawn continuations from within the live body, never from done-callbacks into the same group","Work that must outlive the group belongs to asyncio.create_task on the loop or a new group","Chain successor work inside the task itself before it returns"],"tags":["asyncio","taskgroup","lifecycle","structured-concurrency"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}