Nodes/FlowNodes/πŸ“– Generic operation (write)
ComfyUI Node

πŸ“– Generic operation (write)

Mutate lists and dicts in place, in order

By gitmyloΒ·Created 2 years agoΒ·Updated about a year agoΒ· 13
πŸ“– Generic operation (write)
  • First
  • Second
  • no_cache
  • Third
  • Flow
  • First
  • Flow
β—„Actionβ–Ύβ–Ί

Most nodes are pure: value in, value out, nothing changes. "πŸ“– Generic operation (write)" is the exception that FlowNodes deliberately builds on. Its whole job is to change a container - append to a list, set a dict key, assign an attribute - and hand back the same mutated object. It's how you accumulate state in a graph that otherwise has none.

The operations

Only three actions, and they're all mutations:

  • f.append(s) - push Second onto First (a list). Returns First.
  • f[s] = t - set First[key] = Third. Returns First.
  • setattr(f, s, t) - setattr(First, Second, Third). Returns First.

So the inputs line up as: First is the container you're mutating, Second is the index/key/attribute name (or the appended value), and Third is the value to set for the two assignment actions. First in, First out - the node returns the very object it mutated.

The object identity thing

Here's the mechanism that trips people up. The output isn't a copy - it's the same Python object. If you created a list with πŸ“ƒ Create batch, ran it through a write node, and also wired it elsewhere, every reader sees the change. Python passes objects by reference, so one mutation is visible everywhere.

That's a feature and a footgun at the same time:

  • Feature: chain two write nodes and the second one sees the first one's result, so you can build up a list entry by entry.
  • Footgun: because the mutation happens on a shared object, ComfyUI's caching can bite you. The node has a no_cache input (same dummy socket as the rest of the pack) to force it to run every time - without it, a write might not re-execute on a later queue and your accumulated state silently stays stale.

Why you need flow

Ordering is the whole game here. f.append(s) followed by f[s] = t gives a different result than the reverse, and ComfyUI's scheduler won't guess which you meant. The house pattern, straight from the README: use flow to sequence the writes, and "merge flow after the last operation."

Concretely: a 🌊 Activate flow from any β†’ write node β†’ write node β†’ 🌊 Merge flow (bottleneck). The flow wires force execution order; the merge seals it. This is also the pattern for building a list inside a loop - each pass appends, the flow merge is what lets the loop iterate over the same accumulating container.

The realistic use

Pair it with the containers it mutates: βž• Create empty object (fresh list/dict), πŸ“ƒ Create batch (existing list), and the πŸ”Ί persistent dict for cross-run state. And pair it with the read variant - πŸ“– Generic operation - to pull values back out. Together they're a mini data layer inside the graph.

Install

Part of the FlowNodes pack:

cd ComfyUI/custom_nodes
git clone https://github.com/gitmylo/FlowNodes
# restart ComfyUI

or ComfyUI Manager β†’ search FlowNodes β†’ install. No Python dependencies, no models.

Troubleshooting

  • Mutations don't persist across runs - they don't, by default. In-place mutation only lives as long as the object does; if you want cross-run state, use the persistent dict instead.
  • Wrong order / missing writes - you skipped the flow wiring. Sequence the writes with flow and close with a flow merge.
  • f.append(s) on a dict errors - dicts don't have append. Use f[s] = t for dicts, append for lists.
  • A "write" that doesn't write - the Third input is optional; for f[s] = t without it you'd set None. Connect Third when you want a real value.
CategoryπŸ”‚ FlowNodes/function

Inputs (6)

NameTypeDefaultDescription
ActionCOMBO3 options: f[s] = t, setattr(f, s, t), f.append(s)
First*β€”
Second*β€”
no_cacheNO_CACHEβ€”
Thirdopt*β€”
FlowoptFLOWβ€”

Outputs (2)

NameTypeDescription
First*β€”
FlowFLOWβ€”