BD For Each Run
Iterate a typed list across your queue
- input_1
- input_2
- input_3
- input_4
- input_5
- input_6
- input_7
- input_8
- input_9
- input_10
- input_11
- input_12
- input_13
- input_14
- input_15
- input_16
- data
- label
- current_index
- total_count
- is_last
- status
The classic ComfyUI problem this solves: you have five images that each need the same downstream treatment - upscale, save, whatever - and you don't want to wire five parallel chains. BD_ForEachRun gives you up to 16 typed input slots (input_1 through input_16), and each time you queue a Run, it emits the next wired input plus its label. One chain downstream, run N times.
It's the "iterate over data" sibling to the pack's BD Bulk Save (which saves N files in one Run) - the README's rule of thumb is clean: Bulk Save when the downstream is just saving; ForEachRun when the downstream is more complex, like a per-iteration upscale before save, or a Qwen edit that needs to actually re-execute per item. Empty input slots are skipped, and the labels list aligns with the wired (non-empty) inputs.
How it works
State lives per workflow_id inside the node. In sequential mode (default), each queued Run advances to the next wired input, wrapping around with an iteration counter. manual mode picks a specific slot by manual_index. single always emits input_1. The reset toggle is edge-triggered: when it flips from false to true, the next Run starts back at the first wired input.
The node doesn't hold a batch or loop internally - it's stateful across Runs, which is why it suits "queue the workflow N times" workflows.
What comes out
- data - the current input (any type: IMAGE, MASK, STRING, whatever you wired).
- label - the label for this item, with
label_prefixprepended. Default prefix is_, so labelalbedocomes out as_albedo- designed to drop straight into a save node's suffix. - current_index / total_count / is_last - bookkeeping outputs for downstream logic (e.g. "skip this on the last item").
- status - a text line reporting which slot fired and the current iteration.
The typical setup
BD_DerivePBR maps (albedo, normal, roughness, metallic, ao)
→ input_1..input_5 of BD_ForEachRun
labels: "albedo\nnormal\nroughness\nmetallic\nao"
→ queue 5 runs → each emits one map + its label
→ downstream BD SaveFile uses label as the suffix
Queue five times and you've saved all five maps through a single save chain. That's the whole point: the per-item labels mean your save filenames stay correct without you hand-naming anything.
Install
Part of the BrainDead pack:
cd ComfyUI/custom_nodes
git clone https://github.com/BizaNator/ComfyUI-BrainDead
cd ComfyUI-BrainDead
pip install -r requirements.txt
Or search "BrainDead" in ComfyUI Manager and restart. Pure orchestration logic - no extra dependencies, just a current ComfyUI (the pack uses the V3 extension API).
Troubleshooting
- It errors "no inputs wired" - every slot is empty; the node needs at least one. Remember, empty slots are skipped, so a partially wired list just works with fewer items.
- Labels don't line up - labels align with wired inputs after skipping empties, not with slot numbers. If you left a hole in the middle, the label you thought was for
input_3now matchesinput_4. - Two ForEach nodes fight each other - give each a distinct
workflow_id. - It never restarts from the top - that's sequential mode by design; flip
resetor usemanual_index.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| labels | STRING | albedo normal roughness metallic ao | One label per line, aligned with the WIRED inputs (after skipping empty slots). If labels list is shorter than wired inputs, missing labels emit as empty string. |
| modeopt | COMBO | sequential | sequential: cycle one per Run. manual: pick by manual_index. single: always emit input_1. |
| manual_indexopt | INT | 00–15 | — |
| resetopt | BOOLEAN | false | Edge-triggered: when transitions False→True, the next Run starts at the first wired input. |
| workflow_idopt | STRING | default | Use distinct ids if you have multiple ForEach iterators. |
| label_prefixopt | STRING | _ | Prepended to each label in the output. Default '_' so labels become save suffixes (e.g. label='albedo' → output '_albedo'). Set empty for raw labels. |
| input_1opt | * | Input slot #1. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_2opt | * | Input slot #2. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_3opt | * | Input slot #3. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_4opt | * | Input slot #4. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_5opt | * | Input slot #5. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_6opt | * | Input slot #6. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_7opt | * | Input slot #7. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_8opt | * | Input slot #8. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_9opt | * | Input slot #9. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_10opt | * | Input slot #10. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_11opt | * | Input slot #11. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_12opt | * | Input slot #12. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_13opt | * | Input slot #13. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_14opt | * | Input slot #14. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_15opt | * | Input slot #15. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. | |
| input_16opt | * | Input slot #16. Wire any data type (IMAGE, MASK, etc.). Empty slots are skipped. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| data | * | — |
| label | STRING | — |
| current_index | INT | — |
| total_count | INT | — |
| is_last | BOOLEAN | — |
| status | STRING | — |