fix(validate): only enforce promoted hard checks on output-reachable nodes (BE-3406)#565
Conversation
…nodes (BE-3406) ComfyUI's validate_prompt only validates output nodes and their transitive input ancestors; nodes not reachable from any output are pruned and never validated. #551's promoted hard checks (required_input_missing, autogrow_no_slots, below_min/above_max) ran for every recognized node, hard-rejecting a disconnected/incomplete node the server would silently drop (esp. a required LINK input the frontend omits when disconnected). Compute the output-reachable node set by walking input link references backward from every output node, and restrict the required-presence and range hard errors to that set. Out-of-range values on pruned nodes are demoted back to advisory warnings. Edge/shape/enum checks are unchanged (pre-existing behavior, out of scope). Three #551 tests whose single node had no output are updated to wire that node to a SaveImage so the promotion is asserted where the server enforces it.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
Found 4 finding(s).
| Severity | Count |
|---|---|
| 🟠 High | 1 |
| 🟡 Medium | 1 |
| 🟢 Low | 2 |
Panel: 6/8 reviewers contributed findings.
Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)
…ify demoted range warnings (BE-3406) Cursor-review follow-ups on PR #565: - Guard non-dict `inputs` in both the per-input loop and the `_output_reachable_node_ids` walk: a truthy non-dict value slipped past `or {}` and crashed `.items()`/`.values()` with an unhandled traceback instead of returning structured errors. - Screen unhashable class_type (list/dict) in `Graph.node()`, coerce a non-str class_type to "" in the main loop, and route the linked-source lookup through the guarded `self.node()` — a `dict.get` on an unhashable key raised TypeError across the reachability walk and lookup sites. - Fully qualify the `field` of a range violation demoted to a warning on a pruned node (`node.class.input`), matching every other warning's schema that preflight renders via `w["field"]`. Adds TestValidateMalformedInputs (non-dict inputs, unhashable class_type must not crash) and a demoted-warning field-format assertion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🤖 The reviews loop filed Linear follow-up ticket(s) for review thread(s) deferred as out of scope for this PR:
|
ELI-5
ComfyUI's server only checks the nodes that actually feed into an output (like Save Image), plus everything upstream of them. Any node left disconnected — dangling in an exported workflow — is quietly thrown away and never checked. But PR #551 added strict "you're missing a required input" / "value out of range" rejections that fired on every recognized node, including those disconnected ones. So
validatewould hard-reject a workflow the server would happily run (a disconnected node especially loses its required link inputs, which the frontend only serializes when connected). This PR teachesvalidateto do the same pruning the server does: figure out which nodes are reachable from an output, and only apply those promoted hard checks to them.What changed
_output_reachable_node_ids(workflow, graph): seeds from every output node (is_output_node) and walks input link references ([node_id, output_index]) backward — the same link predicate the rest ofvalidate_workflowuses — to compute the transitive-ancestor set the server would validate. Cycle-safe (dedupes via the reachable set); dangling references (node absent from the workflow) simply aren't traversed.validate_workflowrestricts the fix(validate): presence-check required inputs, no-outputs check, hard-error range violations (BE-3357) #551-promoted hard checks to reachable nodes:required_input_missing(_check_required_present) andautogrow_no_slots(_check_autogrow_required) are skipped for unreachable nodes.below_min/above_maxare demoted from hard errors back to advisory warnings on unreachable nodes (matching pre-fix(validate): presence-check required inputs, no-outputs check, hard-error range violations (BE-3357) #551 behavior for those nodes), via a new keyword-onlyrange_is_errorarg on_validate_catalog_value.shape_mismatch,unknown_enum_value,dangling_edge,output_index_out_of_range,edge_type_mismatch,autogrow_bare_input). These predate fix(validate): presence-check required inputs, no-outputs check, hard-error range violations (BE-3357) #551 and were not flagged as over-rejecting; scoping only the newly-promoted checks keeps the diff minimal and avoids broadening behavior the reviewers didn't raise.Tests
New (in
TestValidateServerParity):test_disconnected_node_missing_required_is_pruned— acceptance (a): disconnected node missing every required input, alongside a valid connected chain →valid: true.test_reachable_node_missing_required_still_errors— acceptance (b): node on the output chain missing a required input still hard-errors.test_transitive_ancestor_missing_required_still_errors— a two-hop-upstream ancestor is reachable and still validated (proves the backward walk follows edges, not just direct parents).test_output_node_missing_required_still_errors— an output node itself seeds the reachable set.test_disconnected_out_of_range_demoted_to_warning— out-of-range on a pruned node is a warning, not a hard error; chain stays valid.test_reachable_out_of_range_still_errors— the connected node still hard-errors on out-of-range.Updated:
test_below_min_error,test_above_max_error,test_required_autogrow_with_no_slots_errors(from #551) each had a single non-output node with no output chain — the server would prune those too (they'd hitprompt_no_outputs, never range/required-checked). Wired the tested node to aSaveImageso the promotion is asserted where the server actually enforces it.uv run --extra dev pytest tests/comfy_cli/cql/→ 152 passed.ruff check+ruff format --checkclean.Notes / judgment calls
matt/be-3357-validate-required-inputs, base of this PR) — the promoted hard checks this PR scopes only exist on that branch, not onmain. GitHub will retarget this PR tomainwhen fix(validate): presence-check required inputs, no-outputs check, hard-error range violations (BE-3357) #551 merges.autogrow_bare_input(a mis-wiring/shape check, distinct from the required-_check_autogrow_requiredno-slots check) is likewise left as-is.Deferred from a review thread on #551 (BE-3406).