diff --git a/docs/load-image-metadata.md b/docs/load-image-metadata.md index a90c4e35..0108fb30 100644 --- a/docs/load-image-metadata.md +++ b/docs/load-image-metadata.md @@ -24,11 +24,16 @@ this change and refresh the page. This Python node needs no Vue widget build. model/LoRA names, local resolution status and warnings. The original `report` output remains notes followed by formatted JSON; it is not a pure JSON string. -`model_name`, `sampler_name`, and `scheduler` use COMBO outputs for converted -dropdown inputs in current ComfyUI. `model_name` contains the matching local -checkpoint or diffusion-model filename. The report identifies the resolved type; -connect it to the appropriate loader. Lookup searches both categories regardless -of how the original metadata labels the model. +`model_name`, `sampler_name`, and `scheduler` are declared as untyped (`*`) +outputs so they can feed the dropdown widget inputs on both +**Load Checkpoint**/**KSampler** and the LoRA Manager loaders. Typing them +`COMBO` does not work: ComfyUI only accepts a `COMBO` output into a node that +declares its dropdown as `COMBO`/`IO.Combo`, while classic dropdowns expose a +plain option list, and the server rejects the link with "Return type mismatch +between linked nodes". `model_name` contains the matching local checkpoint or +diffusion-model filename. The report identifies the resolved type; connect it to +the appropriate loader. Lookup searches both categories regardless of how the +original metadata labels the model. For a diffusion-model workflow, connect `model_name` to **Unet Loader (LoraManager)** and select the correct text encoder(s), VAE, latent node and diff --git a/py/nodes/load_image_metadata.py b/py/nodes/load_image_metadata.py index ddd30153..7322ef49 100644 --- a/py/nodes/load_image_metadata.py +++ b/py/nodes/load_image_metadata.py @@ -118,9 +118,16 @@ class LoadImageMetadataLM: "Connect lora_stack to Lora Loader. Convert loader/sampler widgets to inputs for the other outputs. " "Extraction failures use starter defaults and are shown as ERROR messages in readable_report." ) + # model_name, sampler_name and scheduler select a value from a loader or + # sampler dropdown. They must stay untyped (Any, "*"): ComfyUI rejects a + # "COMBO" (and a "STRING") output linked into the classic list-style combo + # inputs used by Load Checkpoint, KSampler, and the LoRA Manager loaders + # (comfy_execution/validation.py refuses a non-string input type), which + # surfaced as "Return type mismatch between linked nodes" at queue time. + # "*" is the same type ComfyUI's own Primitive node uses to feed widgets. RETURN_TYPES = ( - "IMAGE", "MASK", "STRING", "STRING", "COMBO", "LORA_STACK", "STRING", - "INT", "INT", "FLOAT", "COMBO", "COMBO", "INT", "INT", "FLOAT", "STRING", "STRING", "STRING", + "IMAGE", "MASK", "STRING", "STRING", "*", "LORA_STACK", "STRING", + "INT", "INT", "FLOAT", "*", "*", "INT", "INT", "FLOAT", "STRING", "STRING", "STRING", ) RETURN_NAMES = ( "image", "mask", "positive", "negative", "model_name", "lora_stack", "lora_stack_text", diff --git a/tests/nodes/test_load_image_metadata.py b/tests/nodes/test_load_image_metadata.py index 6c53b2f9..57f47802 100644 --- a/tests/nodes/test_load_image_metadata.py +++ b/tests/nodes/test_load_image_metadata.py @@ -61,6 +61,22 @@ def test_full_node_contract_with_real_png_metadata(runtime): assert LoadImageMetadataLM.INPUT_TYPES()["required"]["image"][1]["image_upload"] +def test_value_selector_outputs_stay_untyped(): + """These sockets must not be typed COMBO/STRING. + + Load Checkpoint, KSampler and the LoRA Manager loaders declare their name + dropdowns as plain option lists ("classic" combos). ComfyUI's + comfy_execution.validation rejects any non-string input type for those, so a + COMBO (or STRING) output linked into them fails at queue time with + "Return type mismatch between linked nodes". Only an untyped ("*") output + works, which is also what ComfyUI's own Primitive node uses for widgets. + """ + types = LoadImageMetadataLM.RETURN_TYPES + names = LoadImageMetadataLM.RETURN_NAMES + for output in ("model_name", "sampler_name", "scheduler"): + assert types[names.index(output)] == "*", output + + @pytest.mark.parametrize("extension", ["webp", "jpg"]) def test_exif_parameters_from_real_image(runtime, extension): image_path, *_ = runtime