Skip to content

fix(node-fixer): reconnect inputs to the new node, not its id (#3126) - #3402

Open
StevenLi-phoenix wants to merge 1 commit into
Comfy-Org:mainfrom
StevenLi-phoenix:fix/3126-recreate-string-node-ids
Open

StevenLi-phoenix wants to merge 1 commit into
Comfy-Org:mainfrom
StevenLi-phoenix:fix/3126-recreate-string-node-ids

Conversation

@StevenLi-phoenix

Copy link
Copy Markdown

Fixes #3126

The root cause and the one-argument fix are as @EnragedAntelope diagnosed in the issue.

Problem

On current frontends, "Fix node (recreate)" fails on any node that has a connected input. The console shows TypeError: t.findInputSlot is not a function, and the canvas keeps two copies of the node: the original, still wired, and an unwired replacement.

Root cause

node_info_copy() in js/node_fixer.js re-wired each linked input with:

src_node.connect(link.origin_slot, dest.id, input.name);

NodeId is a string on current frontends. LGraphNode.connect() looks up the target by id only when typeof target_node === 'number'. Any other value is used as the node itself, so the id string got to target_node.findInputSlot(...) and threw. The menu callback adds the replacement node before it calls node_info_copy(), so the throw skipped app.canvas.graph.remove(this) and both nodes stayed on the canvas.

Fix

Pass the node instead of its id, as the output loop in the same function already does (dest.connect(parseInt(i), target_node, link.target_slot)). This is a one-line change.

I did not take the issue's second suggestion, to remove the original before re-wiring. LGraph.remove() disconnects the node's inputs and outputs, and those are the links node_info_copy() reads.

Tests

tests/test_node_fixer_recreate.py lifts the shipped node_info_copy() and the menu callback with tests/js_lift.py. It runs them in node against a small stub graph that has string node ids and the same connect() id-resolution rule as litegraph. It covers:

  • node_info_copy() re-creates the input link and the output link on the new node.
  • The "Fix node (recreate)" callback leaves exactly one node, with the links moved to it.

Red, against the pre-fix file:

mkdir -p /tmp/base && git show origin/main:js/node_fixer.js > /tmp/base/node_fixer.js
MANAGER_JS_DIR=/tmp/base pytest tests/test_node_fixer_recreate.py
# 2 failed: AssertionError: 'TypeError: target_node.findInputSlot is not a function' is not None

Green, and the rest of the suite:

pytest tests/test_node_fixer_recreate.py      # 2 passed
pytest tests --ignore=tests/e2e               # 220 passed, 18 skipped (218 passed, 18 skipped before)
ruff check .                                  # All checks passed!

I did not run tests/e2e. It needs the separate E2E ComfyUI root, and it does not cover node_fixer.js.

Manual check: ComfyUI master (46aad33) with comfyui-frontend-package==1.57.1, python main.py --cpu, and headless Chromium. node_fixer.js was loaded as a web extension. I wired CheckpointLoaderSimple -> KSampler -> VAEDecode. Then I called the KSampler's "Fix node (recreate)" entry from getExtraMenuOptions() in the page.

  • Before: TypeError: t.findInputSlot is not a function. Nodes 1, 2, 3, 4, with KSampler twice. The links still pointed to the original node 2.
  • After: no error. Nodes 1, 3, 4. Links 1 -> 4 and 4 -> 3.

Not covered

Recreating a node inside a subgraph still fails, with a different cause. node_info_copy() reads links from app.graph (the root graph), so it cannot find links that are inside the subgraph. On 1.57.1 it throws Cannot read properties of undefined (reading 'origin_id'). Inputs that come from the subgraph's input node would need their own handling as well, so I left this for a separate change.

The same dest.id line is also in comfyui_manager/js/node_fixer.js on manager-v4.

…Org#3126)

"Fix node (recreate)" passed dest.id to LGraphNode.connect() when it
re-wired each linked input. Node ids are strings on current frontends,
and connect() only looks a target up by id when it is a number, so it
called findInputSlot() on the id string and threw. The replacement node
had already been added, so the original was never removed and the canvas
kept both. Pass the node itself, as the output loop already does.

Fixes Comfy-Org#3126.

Co-authored-by: Claude Opus-5-5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 0c15f4ea-7416-46fc-85cc-fdf58841ea19
📥 Commits

Reviewing files that changed from the base of the PR and between a74929c and ecfecf2.

📒 Files selected for processing: 2
  • js/node_fixer.js
  • tests/test_node_fixer_recreate.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Node recreation now passes the destination node object when restoring an input connection. Node-backed regression tests check input and output link restoration and the recreate menu action.

Changes: Node recreation

Layer / File(s) Summary
Input-link restoration and recreation tests
js/node_fixer.js, tests/test_node_fixer_recreate.py
node_info_copy passes the destination node object to connect when restoring an input link. Tests check link restoration and verify that the recreate menu action leaves the replacement connected and removes the original node.

Possibly related issues

  • Comfy-Org/ComfyUI_frontend#17438 reports the same findInputSlot is not a function failure from passing a string node ID to connect. This change addresses that failure during node recreation. The reported undo-state leak is not covered.

Suggested reviewers: ltdrdata

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to ecfec

The supplied evidence identifies no issue that should block merging the node-recreation fix, subject to normal checks.

Pre-merge checks: Passed 2
Check name Status Explanation
Linked Issues check Passed Issue [#3126] requires "Fix node (recreate)" to work when current frontends use string node IDs. js/node_fixer.js now passes dest to src_node.connect() instead of dest.id. The added tests mode…
Out of Scope Changes check Passed The pull request changes one connection argument in js/node_fixer.js and adds focused regression tests for [#3126]. The tests support the implementation and verify the reported failure path. No unre…
Full details: Linked Issues check

Explanation

Issue [#3126] requires "Fix node (recreate)" to work when current frontends use string node IDs. js/node_fixer.js now passes dest to src_node.connect() instead of dest.id. The added tests model string IDs and verify input and output links move to the replacement. They also verify that the callback leaves one replacement node. The callback still removes the original after copying, but the reported exception no longer interrupts that removal. The issue's subgraph behavior is a separate cause and is outside [#3126]'s described defect.

Full details: Out of Scope Changes check

Explanation

The pull request changes one connection argument in js/node_fixer.js and adds focused regression tests for [#3126]. The tests support the implementation and verify the reported failure path. No unrelated production change is present.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
✨ Simplify code
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@coderabbitai
coderabbitai Bot requested a review from ltdrdata October 22, 2026 03:39

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Fix node (recreate)" throws on frontend 1.47+ (string node ids), leaves duplicate node on canvas

1 participant