π Generic operation (write)
Mutate lists and dicts in place, in order
- First
- Second
- no_cache
- Third
- Flow
- First
- Flow
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)- pushSecondontoFirst(a list). ReturnsFirst.f[s] = t- setFirst[key] = Third. ReturnsFirst.setattr(f, s, t)-setattr(First, Second, Third). ReturnsFirst.
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. Usef[s] = tfor dicts,appendfor lists.- A "write" that doesn't write - the
Thirdinput is optional; forf[s] = twithout it you'd setNone. Connect Third when you want a real value.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| Action | COMBO | 3 options: f[s] = t, setattr(f, s, t), f.append(s) | |
| First | * | β | |
| Second | * | β | |
| no_cache | NO_CACHE | β | |
| Thirdopt | * | β | |
| Flowopt | FLOW | β |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| First | * | β |
| Flow | FLOW | β |