Skip to content

Validate that a node is executable before running the prompt#14649

Open
wei-hai wants to merge 2 commits into
masterfrom
fix/validate-node-executable
Open

Validate that a node is executable before running the prompt#14649
wei-hai wants to merge 2 commits into
masterfrom
fix/validate-node-executable

Conversation

@wei-hai

@wei-hai wei-hai commented Jun 26, 2026

Copy link
Copy Markdown

Problem

A node whose FUNCTION points at a method that doesn't exist (e.g. a typo in a custom node), or a V3 node missing its execute override, is only detected when that node runs — after every upstream node has already executed. In a multi-node workflow the user waits for the whole graph to run up to the broken node before seeing the error, even though the problem is statically knowable before anything runs.

Fix

validate_prompt already walks every node before execution (checking class_type is present and the node class is installed). This adds an executability check in that same loop: if a node's declared entry point doesn't resolve to a callable, the prompt is rejected up front with the error attributed to the offending node (returned in node_errors), and nothing runs.

  • V1 nodes: resolve the FUNCTION method on the class (the common case), falling back to an instance — the runtime invokes the method on an instance (getattr(obj, obj.FUNCTION)), and a node may legitimately define FUNCTION or its method in __init__. The instance fallback only runs when the class-level check fails, so normal nodes are never instantiated during validation.
  • V3 nodes: checked via their existing VALIDATE_CLASS (execute/define_schema overrides), which is pure MRO introspection — cheap and side-effect free.

The check is placed before the loop's OUTPUT_NODE access, which for a structurally broken V3 node would otherwise raise uncaught — so this also turns a pre-existing latent failure into a clean, node-attributed error.

Tests

Adds tests-unit/execution_test/validate_node_executable_test.py: V1 typo and V3 typo are rejected with a per-node error; good V1/V3 nodes pass; and a node that defines its method in __init__ is not falsely rejected (guards the instance-fallback path).

Note

This is the validation-time counterpart to #14648, which makes runtime/scheduling errors surface to the client instead of silently killing the worker thread. The two are independent (different files) and complementary: this rejects statically-detectable breakage up front; #14648 is the catch-all for errors only knowable at runtime.

A node whose FUNCTION points at a method that does not exist (e.g. a typo in
a custom node), or a V3 node missing its execute override, was only detected
once that node ran -- after every upstream node had already executed. In a
multi-node workflow the user waited for the whole graph to run up to the
broken node before seeing the error.

validate_prompt already walks every node before execution; add an
executability check there so the error is reported up front and attributed
to the offending node (returned in node_errors), and nothing runs.

The check resolves the V1 FUNCTION method on the class (the common case) and
falls back to an instance, since the runtime invokes it on an instance and a
node may define FUNCTION or its method in __init__. V3 nodes are checked via
their existing VALIDATE_CLASS.

Add tests for V1 typo, V3 typo, good nodes, and a node whose method is
defined in __init__ (must not be falsely rejected).
@wei-hai wei-hai added the cursor-review Trigger multi-model Cursor code review label Jun 26, 2026
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 467b7fdb-8d1a-4518-a20c-35b9cfa46d31

📥 Commits

Reviewing files that changed from the base of the PR and between 82c954b and bf00c39.

📒 Files selected for processing (2)
  • execution.py
  • tests-unit/execution_test/validate_node_executable_test.py

📝 Walkthrough

Walkthrough

validate_prompt now checks whether each candidate output node resolves to an executable entry point before schema-derived validation continues. New helpers handle V1 FUNCTION resolution and V3 VALIDATE_CLASS() executability checks, and non-executable nodes now return invalid_node_definition with node_errors. New unit tests cover valid and invalid V1 and V3 nodes, including an instance-assigned callable case.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: validating node executability before prompt execution.
Description check ✅ Passed The description matches the implemented validation change, including V1/V3 handling and the added tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests-unit/execution_test/validate_node_executable_test.py (1)

88-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Restore NODE_CLASS_MAPPINGS entries after each test.

These tests permanently mutate the global node registry, so later tests run with extra classes registered. Please wrap registration in a fixture or try/finally that restores the previous value.

Possible fix
 def _register(class_type, class_def):
-    nodes.NODE_CLASS_MAPPINGS[class_type] = class_def
+    old = nodes.NODE_CLASS_MAPPINGS.get(class_type)
+    nodes.NODE_CLASS_MAPPINGS[class_type] = class_def
+    return old
+
+
+def _restore(class_type, old):
+    if old is None:
+        nodes.NODE_CLASS_MAPPINGS.pop(class_type, None)
+    else:
+        nodes.NODE_CLASS_MAPPINGS[class_type] = old
 def test_good_node_passes():
-    _register("GoodV1Node", _GoodV1Node)
-    assert node_not_executable_reason(_GoodV1Node, "GoodV1Node") is None
-    valid, _, _, _ = _validate("GoodV1Node")
-    assert valid is True
+    old = _register("GoodV1Node", _GoodV1Node)
+    try:
+        assert node_not_executable_reason(_GoodV1Node, "GoodV1Node") is None
+        valid, _, _, _ = _validate("GoodV1Node")
+        assert valid is True
+    finally:
+        _restore("GoodV1Node", old)

Also applies to: 97-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests-unit/execution_test/validate_node_executable_test.py` around lines 88 -
90, The helper `_register` in the validate_node_executable tests mutates the
global `nodes.NODE_CLASS_MAPPINGS` and leaves added entries behind across tests.
Update the registration flow used by these test cases to save the original
mapping state before adding classes and restore it afterward, ideally via a
fixture or a `try/finally` around the test setup that calls `_register` so each
test starts with the same registry.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@execution.py`:
- Around line 1138-1145: The validation in execution.py is eagerly constructing
the class via class_def(), which triggers __init__ and can misclassify valid
nodes as invalid. Update the logic around _v1_function_resolves and the FUNCTION
lookup to only inspect the class object itself without instantiating it, and
keep the existing class_def/class_type error reporting path intact.

---

Nitpick comments:
In `@tests-unit/execution_test/validate_node_executable_test.py`:
- Around line 88-90: The helper `_register` in the validate_node_executable
tests mutates the global `nodes.NODE_CLASS_MAPPINGS` and leaves added entries
behind across tests. Update the registration flow used by these test cases to
save the original mapping state before adding classes and restore it afterward,
ideally via a fixture or a `try/finally` around the test setup that calls
`_register` so each test starts with the same registry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4037bfa0-7af9-455d-9bd7-2ca5f9b7d48f

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb784e and 82c954b.

📒 Files selected for processing (2)
  • execution.py
  • tests-unit/execution_test/validate_node_executable_test.py
Comment thread execution.py Outdated
Addresses review feedback: the V1 executability check fell back to
constructing the node (class_def()) when the FUNCTION method wasn't found on
the class. That runs __init__ during validation, so a constructor's side
effects or failure could be misreported as invalid_node_definition for an
otherwise valid node.

Inspect only the class. No core/extra node defines its FUNCTION method on the
instance, so this loses no real coverage while removing the side-effect risk.

Replace the instance-fallback test with one asserting a node with a raising
__init__ but a valid class-level method still passes validation (i.e. it is
never instantiated).
Comment thread execution.py
# point resolves to a real method) before we touch any schema-derived
# attributes below or start execution. Catches code typos up front and
# attributes the error to the offending node.
not_executable = node_not_executable_reason(class_, class_type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is validating the class anyway, what do you think of just doing this validation when we register a node instead of doing it on every execution?
This isn't a user error -- it's always a custom node author error, so making the error appear to the author during startup seems a lot more valuable than only surfacing it to the UI once the node is actually used.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how we make it appear to the author, is there a place to store the author's contact when registering the custom node?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just mean showing it in the console on startup since the custom node author is virtually guaranteed to start up ComfyUI after they make changes (even if they don't execute every one of their own nodes).

This isn't the sort of issue that might occur on users' machines when it worked perfectly fine on the author's -- it's a static error in typing structure.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the error does show in the console, this PR makes sure the error is also rendered on frontend in case the console logs are noisy.

@wei-hai wei-hai requested a review from guill July 1, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cursor-review Trigger multi-model Cursor code review

3 participants