Nodes/ComfyUI-Utility-Suite/List Accumulator Append
ComfyUI Node

List Accumulator Append

A Bag You Can Keep Adding To Inside a Loop

By tom-m-2020·Created about a month ago·Updated 7 days ago· 1
List Accumulator Append
  • items
  • accumulator
  • accumulator

ComfyUI has lists, and lists give you a for-each loop - a list input runs the node once per item. What it does not give you is an accumulator: a place to put the results as you go. The obvious implementation, a mutable list that nodes keep appending to, is the one thing the engine's execution model punishes. The graph decides what to re-run by comparing values, and a value that mutates in place looks unchanged while holding different contents - so a loop built on a mutable accumulator either doubles your data or hands you a stale copy, depending on which node ran when. This node implements the other approach: an immutable one.

How it works

The accumulator is a frozen dataclass holding a tuple. It has exactly one interesting method, append_items, which returns a new accumulator with the old items plus the new ones - it never modifies the one you passed in. Nothing is copied deeply, but nothing is aliased in a way that matters either, because nothing can be changed after creation.

That immutability is not pedantry, it is the mechanism. Each iteration of your loop produces a distinct value, so the engine can cache and compare them correctly, and there is no window in the execution order where two branches see different versions of the same object. It is the only way an accumulator survives contact with a graph that executes out of order and aggressively caches.

The nodes are a matched pair, and the type is opaque - UTILITY_ANY_LIST, not a plain list. That is intentional: the socket cannot be confused with a normal list input, so the engine will not try to map over your accumulator item by item.

Inputs and outputs

items and an optional accumulator in; one accumulator out.

  • items is a list input that accepts anything (*). Hand it the output of the branch you just ran - a list of images, masks, SEGS, boxes, whatever - and the whole container arrives in one execution.
  • accumulator is optional. On the first pass of a loop, leave it unconnected and you start from an empty accumulator. On every pass after that, wire the node's output back into its own input.
  • The output is the updated accumulator. It carries items untouched, so it works identically for any type - it never looks at what is inside.

The wiring is the whole tutorial: output → the optional input of the same node, through whatever loop construct you are using (a for-each, a repeat, a While-style pack, or the classic trick of feeding the list back through a *-typed socket).

Why not just use a list-append node

Because the value would be a plain list, and a plain list socket is a list input, which means the engine maps the receiving node over its items. You would get one accumulator per item instead of one accumulator per iteration - a class of bug that is genuinely hard to see on the canvas, because the wire looks right and the node count changes silently. The typed, opaque accumulator is what makes "one bag, many passes" expressible at all.

When the loop is done, convert it with List Accumulator To List to get a real Comfy list out, and everything downstream runs per item again.

Install

Part of ComfyUI-Utility-Suite. ComfyUI Manager → search ComfyUI-Utility-Suite → install → restart, or:

cd ComfyUI/custom_nodes
git clone https://github.com/tom-m-2020/ComfyUI-Utility-Suite

Restart ComfyUI. No models, no downloads, and the declared opencv-python-headless dependency is irrelevant here. The pack is written against ComfyUI's newer V3 node API, so run a current backend.

Traps

It has to be fed back into itself. If the accumulator input is unwired, every execution starts from an empty bag and you end up with only the last iteration's items - which looks like "the loop overwrites everything" rather than like a missing wire.

Wrong type in the socket fails loudly. Pass an accumulator-shaped nothing, or a plain Python list where the accumulator is expected, and you get a type error naming what it got. That is better than the alternative, but it does mean you cannot smuggle a plain list through this type.

Opaque means uninspectable. You cannot see inside an accumulator on the canvas. Convert to a list and drop List / Batch Inspector on it if you want a count before you commit to a workflow that saves forty files.

Appending an empty list is a silent no-op. If your loop's branch returned nothing for an iteration, the accumulator simply does not grow - correct, and occasionally the reason a count is off by one from your expectation.

CategoryUtility Suite/Utilities

Inputs (2)

NameTypeDefaultDescription
items*—
accumulatoroptUTILITY_ANY_LIST—

Outputs (1)

NameTypeDescription
accumulatorUTILITY_ANY_LIST—