From 346ee898cb156f12be4c84684214b3ac22a25fb7 Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Mon, 1 Jun 2026 19:18:10 -0700 Subject: [PATCH] DynamicSlot: forbid AnyType inside list/MultiType when Grouping AnyType with concrete types conflates the known-type case with the unresolvable-wildcard case under one branch and muddies slotType. AnyType must stand alone as when=io.AnyType so the two states stay distinct in both dispatch and the serialized schema. Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501 Co-authored-by: Amp --- comfy_api/latest/_io.py | 13 ++++++++++++- tests-unit/comfy_api_test/test_dynamic_slot.py | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/comfy_api/latest/_io.py b/comfy_api/latest/_io.py index a736863fd..4a80ed138 100644 --- a/comfy_api/latest/_io.py +++ b/comfy_api/latest/_io.py @@ -1242,7 +1242,13 @@ class DynamicSlot(ComfyTypeI): if isinstance(when, type) and issubclass(when, _ComfyType): return (when.io_type,) if isinstance(when, MultiType.Input): - return tuple(dict.fromkeys(t.io_type for t in when.io_types)) + result = tuple(dict.fromkeys(t.io_type for t in when.io_types)) + if "*" in result and len(result) > 1: + raise ValueError( + "DynamicSlot.Option: AnyType cannot be grouped with concrete types; " + "use a separate Option(when=io.AnyType, ...) instead" + ) + return result if isinstance(when, Iterable) and not isinstance(when, str): types: list[str] = [] for t in when: @@ -1254,6 +1260,11 @@ class DynamicSlot(ComfyTypeI): types.append(t.io_type) if not types: raise ValueError("DynamicSlot.Option: when=[] is not allowed; use when=None instead") + if "*" in types and len(types) > 1: + raise ValueError( + "DynamicSlot.Option: AnyType cannot be grouped with concrete types; " + "use a separate Option(when=io.AnyType, ...) instead" + ) return tuple(types) raise ValueError( "DynamicSlot.Option: when must be None, a ComfyType class, a list of ComfyType classes, " diff --git a/tests-unit/comfy_api_test/test_dynamic_slot.py b/tests-unit/comfy_api_test/test_dynamic_slot.py index c4444d745..b2e810116 100644 --- a/tests-unit/comfy_api_test/test_dynamic_slot.py +++ b/tests-unit/comfy_api_test/test_dynamic_slot.py @@ -69,6 +69,19 @@ def test_option_when_list_with_non_comfytype_rejected(): io.DynamicSlot.Option(when=[io.Image, "MASK"], inputs=[]) +def test_option_when_list_with_anytype_rejected(): + """AnyType must stand alone — it represents the unresolvable-wildcard + state, not a concrete type that can share a branch with concrete types.""" + with pytest.raises(ValueError, match="AnyType cannot be grouped"): + io.DynamicSlot.Option(when=[io.Image, io.AnyType], inputs=[]) + + +def test_option_when_multitype_with_anytype_rejected(): + mt = io.MultiType.Input("x", types=[io.Image, io.AnyType]) + with pytest.raises(ValueError, match="AnyType cannot be grouped"): + io.DynamicSlot.Option(when=mt, inputs=[]) + + # --------------------------------------------------------------------------- # DynamicSlot.Input construction and serialization # ---------------------------------------------------------------------------