Skip to content

Internal error from nursery mis-nesting (due to bad use of AsyncExitStack) #3298

@Zac-HD

Description

@Zac-HD

contextlib.AsyncExitStack is quite dangerous, in that it makes it very easy to break the nesting structure of context managers. If you manage to do that to a nursery, bad things result:

from contextlib import AsyncExitStack, asynccontextmanager
import trio

async def main():
    async with AsyncExitStack() as stack, trio.open_nursery() as nursery:
        # The asynccontextmanager is going to create a nursery that outlives this nursery!
        nursery.start_soon(
            stack.enter_async_context,
            asynccontextmanager_that_creates_a_nursery_internally(),
        )

@asynccontextmanager
async def asynccontextmanager_that_creates_a_nursery_internally():
    async with trio.open_nursery() as nursery:
        nursery.start_soon(
            print_sleep_print, "task_in_asynccontextmanager_nursery", 2.0
        )
        yield

async def print_sleep_print(name: str, sleep_time: float):
    print(f"{name} is about to sleep for {sleep_time} seconds")
    await trio.sleep(sleep_time)
    print(f"{name} finished sleeping for {sleep_time} seconds")

if __name__ == "__main__":
    trio.run(main)
$ python t.py
task_in_asynccontextmanager_nursery is about to sleep for 2.0 seconds
Traceback (most recent call last):
  File ".../python3.11/site-packages/trio/_core/_run.py", line 2872, in unrolled_run
    runner.task_exited(task, final_outcome)
  File ".../python3.11/site-packages/trio/_core/_run.py", line 2038, in task_exited
    self.tasks.remove(task)
KeyError: <Task 'contextlib.AsyncExitStack.enter_async_context' at 0x101b1f3d0>

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/zac/code/anthropic/t.py", line 34, in <module>
    trio.run(main)
  File ".../python3.11/site-packages/trio/_core/_run.py", line 2518, in run
    timeout = gen.send(next_send)
              ^^^^^^^^^^^^^^^^^^^
  File ".../python3.11/site-packages/trio/_core/_run.py", line 2928, in unrolled_run
    raise TrioInternalError("internal error in Trio - please file a bug!") from exc
trio.TrioInternalError: internal error in Trio - please file a bug!
Exception ignored in: <coroutine object Runner.init at 0x101b2f940>
Traceback (most recent call last):
  File ".../python3.11/site-packages/trio/_core/_run.py", line 2136, in init
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1109, in __aexit__
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1282, in _nested_child_finished
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1249, in _add_exc
  File ".../python3.11/site-packages/trio/_core/_run.py", line 930, in _cancel
  File ".../python3.11/site-packages/trio/_core/_run.py", line 501, in recalculate
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1663, in _attempt_delivery_of_any_pending_cancel
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1636, in _attempt_abort
  File ".../python3.11/site-packages/trio/_core/_io_kqueue.py", line 164, in abort
  File ".../python3.11/site-packages/trio/_core/_io_kqueue.py", line 186, in abort
ValueError: I/O operation on closed kqueue object
Exception ignored in: <function Nursery.__del__ at 0x100f8b100>
Traceback (most recent call last):
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1480, in __del__
AssertionError: 
Exception ignored in: <function Nursery.__del__ at 0x100f8b100>
Traceback (most recent call last):
  File ".../python3.11/site-packages/trio/_core/_run.py", line 1480, in __del__
AssertionError: 
Exception ignored in: <async_generator object asynccontextmanager_that_creates_a_nursery_internally at 0x102186340>
Traceback (most recent call last):
  File ".../python3.11/site-packages/trio/_core/_asyncgens.py", line 112, in finalizer
  File ".../python3.11/site-packages/trio/_core/_entry_queue.py", line 148, in run_sync_soon
  File ".../python3.11/site-packages/trio/_core/_wakeup_socketpair.py", line 39, in wakeup_thread_and_signal_safe
OSError: [Errno 9] Bad file descriptor

and I think we should check for misnesting on nursery exit so that we can deliver an immediate TrioRuntimeError (new type?) rather than an internal error further down the line.

It would also be really helpful to (optionally) dump the stack trace of both nurseries as-of-entry, making it obvious exactly where the mis-nesting started.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions