diff --git a/tractor/_root.py b/tractor/_root.py index 403907ec..a7b4d4d2 100644 --- a/tractor/_root.py +++ b/tractor/_root.py @@ -280,10 +280,14 @@ async def open_root_actor( # start the actor runtime in a new task async with trio.open_nursery() as nursery: - # ``_runtime.async_main()`` creates an internal nursery and - # thus blocks here until the entire underlying actor tree has - # terminated thereby conducting structured concurrency. - + # ``_runtime.async_main()`` creates an internal nursery + # and blocks here until any underlying actor(-process) + # tree has terminated thereby conducting so called + # "end-to-end" structured concurrency throughout an + # entire hierarchical python sub-process set; all + # "actor runtime" primitives are SC-compat and thus all + # transitively spawned actors/processes must be as + # well. await nursery.start( partial( async_main, diff --git a/tractor/_runtime.py b/tractor/_runtime.py index d2a9e405..ed1e5f85 100644 --- a/tractor/_runtime.py +++ b/tractor/_runtime.py @@ -1514,13 +1514,22 @@ async def async_main( # - root actor: the ``accept_addr`` passed to this method assert accept_addrs - actor._server_n = await service_nursery.start( - partial( - actor._serve_forever, - service_nursery, - listen_sockaddrs=accept_addrs, + try: + actor._server_n = await service_nursery.start( + partial( + actor._serve_forever, + service_nursery, + listen_sockaddrs=accept_addrs, + ) ) - ) + except OSError as oserr: + # NOTE: always allow runtime hackers to debug + # tranport address bind errors - normally it's + # something silly like the wrong socket-address + # passed via a config or CLI Bo + entered_debug = await _debug._maybe_enter_pm(oserr) + raise + accept_addrs: list[tuple[str, int]] = actor.accept_addrs # NOTE: only set the loopback addr for the