EzFlex-LoopEnd
The round counter you actually edit, and how the loop really stops
- value1
- value2
- value3
- value4
- value5
- value6
- value7
- value8
- value9
- value10
- value11
- value12
- value13
- value14
- value15
- value16
- value17
- value18
- value19
- value20
- out1
- out2
- out3
- out4
- out5
- out6
- out7
- out8
- out9
- out10
- out11
- out12
- out13
- out14
- out15
- out16
- out17
- out18
- out19
- out20
EzFlex-LoopEnd looks like the boring half of the pair - no index output, no initial values, just a number and a row of ports. It's the half that does the work. This is the node ComfyUI re-executes at the end of every round to decide whether there is a next round, and the node that hands the engine an expanded copy of your loop body when the answer is yes.
Worth knowing why it's built this way: ComfyUI's queue has no notion of iteration. The graph is walked once. So the trick every loop pack uses is to hand the executor a different prompt instead of asking it to go around again - clone the body, bump the round marker, write the previous round's values straight into the clone's inputs. LoopEnd is where that happens. It also has to declare itself an output node, and the source comments are blunt about why: if nothing hangs off its outputs, the engine never schedules it, the expansion never fires, and your loop silently runs one round. Core's own loop nodes mark LoopResult the same way.
The fields and ports
rounds is the only widget, and it's the round counter everybody asks about. Inputs value1 … value20 are dynamic and one-to-one with LoopStart's outputs: whatever a port receives becomes the next round's LoopStart.valueN. Outputs out1 … out20 return those same values as-is once the loop is finished, for the final decode or stitch.
Two details on the counter that will save you an hour:
- It counts total passes, including the first one. The shipped 1.3.0 code stops when the current round marker plus one reaches
rounds, and the marker starts at 0. Sorounds = 6gives you six passes. The tiny label next to the field ("1 = run once more") implies an off-by-one the engine doesn't implement, and the panel's own badge disagrees with its own label. IfEzFlex-TimeLinetold you there are six segments, put 6. - 0 turns looping off. One pass, plus a console line telling you rounds is 0. Not an error, just a quiet "you didn't ask for a loop."
How a round actually expands
On each execution the node works out the loop body by walking downstream from the paired LoopStart and stopping at itself - same reachability rule the kernel's validator uses. It then clones every node in that region, re-points the wires that referenced loop-internal nodes at the clones, writes cur + 1 into the next LoopStart's index, and writes each valueK it received into the clone's _ezfeedK input as a literal. Wires that came from outside the region are left alone, so an external model loader is reused rather than reloaded. The result is handed back as an expanded graph and the engine runs it. Next round, same thing, until the counter runs out.
Both loop nodes return NaN from IS_CHANGED, which is the documented way to say "never cache me" - otherwise re-queuing the same workflow would hit the cache and skip the whole loop. There's also a hard safety cap at 200 rounds: if the marker is ever lost, the loop stops instead of freezing your canvas.
Install
Standard pack install: ComfyUI Manager → search EzFlex (title Comfyui-EzFlex-Presets) → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/blackbossokok/Comfyui-EzFlex-Presets
# restart ComfyUI
Only mutagen is needed beyond what ComfyUI ships (pip install -r requirements.txt); the llama-cpp-python / gguf / onnx extras are opt-in so Manager never triggers a build. ComfyUI 0.3.13+.
Common issues
- It loops forever, or the canvas crawls. You've hit the 200-round cap and it's re-expanding every round. Check the counter and make sure the paired LoopStart is still on the canvas - the terminal tells you which of the two it is.
- Nothing loops. Either the counter is 0, or there's no LoopStart for it to pair with (the log says so). The pairing is by graph position, not by a wire: don't expect to fix it by adding a link.
Dependency cycle detected. You wired LoopEnd back into LoopStart. The feed is implicit and automatic; the wire is an actual cycle and the kernel rejects it.- The preview only ever shows the last segment. Expected.
outNis the last round's carried value, not a collection, and a preview node is a terminal. If you want the whole set, carry it: route the round's product throughEzFlex-MergeListand feed the merged list into avalueKport, so it comes back out complete after the final round. - Round two loses the body. Almost always an internal wire you re-pointed at something outside the loop region - the expander only clones what sits between Start and End.
The habit that keeps this node predictable: treat it as the loop's closing brace. Everything inside Start → End is the body; everything crossing the End's valueK ports is what survives into the next round; everything coming out of outN is the final answer.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| rounds | INT | 1 | Total rounds including the first one. 0 or 1 = a single pass (plain run); N = N passes total. |
| value1opt | * | Loop data 1. Whatever this port receives becomes the next round's LoopStart value1 (implicit feed, do not wire End back to Start). | |
| value2opt | * | Loop data 2. Whatever this port receives becomes the next round's LoopStart value2 (implicit feed, do not wire End back to Start). | |
| value3opt | * | Loop data 3. Whatever this port receives becomes the next round's LoopStart value3 (implicit feed, do not wire End back to Start). | |
| value4opt | * | Loop data 4. Whatever this port receives becomes the next round's LoopStart value4 (implicit feed, do not wire End back to Start). | |
| value5opt | * | Loop data 5. Whatever this port receives becomes the next round's LoopStart value5 (implicit feed, do not wire End back to Start). | |
| value6opt | * | Loop data 6. Whatever this port receives becomes the next round's LoopStart value6 (implicit feed, do not wire End back to Start). | |
| value7opt | * | Loop data 7. Whatever this port receives becomes the next round's LoopStart value7 (implicit feed, do not wire End back to Start). | |
| value8opt | * | Loop data 8. Whatever this port receives becomes the next round's LoopStart value8 (implicit feed, do not wire End back to Start). | |
| value9opt | * | Loop data 9. Whatever this port receives becomes the next round's LoopStart value9 (implicit feed, do not wire End back to Start). | |
| value10opt | * | Loop data 10. Whatever this port receives becomes the next round's LoopStart value10 (implicit feed, do not wire End back to Start). | |
| value11opt | * | Loop data 11. Whatever this port receives becomes the next round's LoopStart value11 (implicit feed, do not wire End back to Start). | |
| value12opt | * | Loop data 12. Whatever this port receives becomes the next round's LoopStart value12 (implicit feed, do not wire End back to Start). | |
| value13opt | * | Loop data 13. Whatever this port receives becomes the next round's LoopStart value13 (implicit feed, do not wire End back to Start). | |
| value14opt | * | Loop data 14. Whatever this port receives becomes the next round's LoopStart value14 (implicit feed, do not wire End back to Start). | |
| value15opt | * | Loop data 15. Whatever this port receives becomes the next round's LoopStart value15 (implicit feed, do not wire End back to Start). | |
| value16opt | * | Loop data 16. Whatever this port receives becomes the next round's LoopStart value16 (implicit feed, do not wire End back to Start). | |
| value17opt | * | Loop data 17. Whatever this port receives becomes the next round's LoopStart value17 (implicit feed, do not wire End back to Start). | |
| value18opt | * | Loop data 18. Whatever this port receives becomes the next round's LoopStart value18 (implicit feed, do not wire End back to Start). | |
| value19opt | * | Loop data 19. Whatever this port receives becomes the next round's LoopStart value19 (implicit feed, do not wire End back to Start). | |
| value20opt | * | Loop data 20. Whatever this port receives becomes the next round's LoopStart value20 (implicit feed, do not wire End back to Start). | |
| __ezroundopt | INT | 0 | Internal round marker written by the loop expander. Not a canvas port. |
Outputs (20)
| Name | Type | Description |
|---|---|---|
| out1 | * | — |
| out2 | * | — |
| out3 | * | — |
| out4 | * | — |
| out5 | * | — |
| out6 | * | — |
| out7 | * | — |
| out8 | * | — |
| out9 | * | — |
| out10 | * | — |
| out11 | * | — |
| out12 | * | — |
| out13 | * | — |
| out14 | * | — |
| out15 | * | — |
| out16 | * | — |
| out17 | * | — |
| out18 | * | — |
| out19 | * | — |
| out20 | * | — |