From d036ef7d7fd6b1d80f3ed41b2ff4dd79ef32db15 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 6 May 2026 15:13:02 -0400 Subject: [PATCH] Add `enable_transports`/`registry_addrs` proto guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raise `ValueError` from `open_root_actor()` when any `registry_addrs` entry uses a transport proto not in `enable_transports` — historically this caused a silent indefinite hang during the registrar handshake (the actor could never connect to register/discover). Also, - update `test_root_passes_tpt_to_sub` to detect a proto mismatch between parametrized `tpt_proto_key` and CLI `tpt_proto`, asserting the new guard raises `ValueError` with expected msg content. - replace old commented-out notes with a clearer explanation of the mismatch foot-gun. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/ipc/test_multi_tpt.py | 27 ++++++++++++++++++++------- tractor/_root.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/tests/ipc/test_multi_tpt.py b/tests/ipc/test_multi_tpt.py index 94dae213..57d8ecf3 100644 --- a/tests/ipc/test_multi_tpt.py +++ b/tests/ipc/test_multi_tpt.py @@ -59,15 +59,18 @@ async def chk_tpts( ) def test_root_passes_tpt_to_sub( tpt_proto_key: str, + tpt_proto: str, reg_addr: tuple, debug_mode: bool, ): - # XXX NOTE, the `reg_addr` addr won't be the same type as the - # `tpt_proto_key` would deliver here unless you pass `--tpt-proto - # ` on the CLI. - # - # if tpt_proto_key == 'uds': - # breakpoint() + # `reg_addr` is sourced from the CLI `--tpt-proto={tpt_proto}`, + # so when the parametrized `tpt_proto_key` differs, the test + # asks the runtime to `enable_transports=[]` while + # pointing `registry_addrs` at a `reg_addr` of the wrong proto. + # The layer-2 guard in `open_root_actor` is expected to fail + # fast with `ValueError` on this mismatch (rather than the prior + # silent hang during the registrar handshake). + proto_mismatch: bool = (tpt_proto_key != tpt_proto) async def main(): async with tractor.open_nursery( @@ -99,4 +102,14 @@ def test_root_passes_tpt_to_sub( # shudown sub-actor(s) await an.cancel() - trio.run(main) + if proto_mismatch: + # mismatched proto must raise `ValueError` from the + # `open_root_actor` runtime guard before any subactor spawn. + with pytest.raises(ValueError) as excinfo: + trio.run(main) + msg: str = str(excinfo.value) + assert 'enable_transports' in msg + assert 'registry_addrs' in msg + assert tpt_proto_key in msg or tpt_proto in msg + else: + trio.run(main) diff --git a/tractor/_root.py b/tractor/_root.py index 958ed503..29225957 100644 --- a/tractor/_root.py +++ b/tractor/_root.py @@ -371,6 +371,34 @@ async def open_root_actor( for uw_addr in uw_reg_addrs ] + # fail-fast on `enable_transports` / `registry_addrs` proto + # mismatch — historically this caused a silent indefinite + # hang during the registrar handshake (registry was reachable + # only via a transport not in `enable_transports`, so the + # actor could never connect to register/discover). See + # `tests/ipc/test_multi_tpt.py::test_root_passes_tpt_to_sub` + # for the foot-gun case + its layer-1 skip-guard. + bad_addrs: list[tuple[str, Address]] = [ + (addr.proto_key, addr) + for addr in registry_addrs + if addr.proto_key not in enable_transports + ] + if bad_addrs: + raise ValueError( + f'`registry_addrs` contains addr(s) whose proto is ' + f'not in `enable_transports`!\n' + f'enable_transports: {enable_transports!r}\n' + f'mismatched_addrs:\n' + + '\n'.join( + f' - proto_key={pk!r} addr={a!r}' + for pk, a in bad_addrs + ) + + '\n\n' + f'Either add the missing proto to ' + f'`enable_transports`, or remove the addr from ' + f'`registry_addrs`.' + ) + # Debug-mode is currently only supported for backends whose # subactor root runtime is trio-native (so `tractor.devx. # debug._tty_lock` works). See `_DEBUG_COMPATIBLE_BACKENDS`