Checkpoint Folder Iterator
One Prompt, Every Checkpoint in Your Folder, One Model in VRAM
- model
- clip
- vae
- ckpt_name
- label
- index
- count
The problem this solves
The best model-comparison posts on the SD subreddits are the same shape: one prompt, one seed, a dozen checkpoints, one grid. "Tested 23 realistic models. Here are the best 8 results compared" (+1219) is a format people keep re-inventing because it's useful and miserable to produce. The usual method is a graph with a Checkpoint Loader per model, bypassing all but one, queue, save, un-bypass the next, repeat - or wiring them all in and letting ComfyUI try to hold 6.5GB of SDXL per loader.
Checkpoint Folder Iterator turns that into a loop. Point it at a folder, set index to 0 with the control set to increment, queue as many runs as you have files; each run loads the next checkpoint, one model in memory at a time, same prompt and seed throughout. "Which of these twelve Illustrious merges do I actually like" stops being an afternoon of clicking.
How it works
Mechanically it's simple, which is why it's reliable. The node scans every .safetensors underneath models/checkpoints/<your folder> - recursively, so subfolders are included - and sorts the names naturally, so v2 comes before v10 instead of after it. Then it takes the entry at index and loads it through comfy.sd.load_checkpoint_guess_config, passing ComfyUI's embeddings directory so embedding:name in your prompt still resolves.
The detail worth knowing: it refuses to wrap around. Ask for index 12 in a 10-file folder and the run fails with "Index 12 is past the end: only 10 checkpoints matched" rather than quietly starting over - which matters, because a wrap would reload your first model and, with Model Compare Sheet paired in, archive the grid you just spent an hour building.
The inputs that matter (there are two)
folder is relative to models/checkpoints - . for the root itself, sdxl/realistic for a subfolder - or an absolute path anywhere on disk, which is what you want when your models live on a second drive. (Relative paths can't climb out with ..; that gets warned about and skipped, so use the absolute form.)
index is the entire API. Set it to 0, flip the dropdown under it to increment, set the batch-count box next to Queue to your checkpoint count, queue. Count them first:
ls -1 ComfyUI/models/checkpoints/*.safetensors | wc -l
The two optional fields, include_pattern and exclude_pattern, are regexes on the relative filename - illustrious to sweep one family, inpaint in the exclude slot to skip variants you never compare.
Outputs: model, clip and vae go exactly where your Checkpoint Loader used to connect. ckpt_name is the relative filename, label the bare filename stem (juggernautXL_v10), index and count where the sweep is. Wire label and index into Model Compare Sheet; count is your queue number.
Install
ComfyUI Manager, search "ComfyUI-KikoTools", install, restart. Or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/ComfyAssets/ComfyUI-KikoTools.git
Then restart and look under ComfyAssets β xyz-helpers. The README also tells you to run pip install -r requirements-dev.txt - the dev set, pytest and linters. Don't. Runtime requirements.txt has one item, google-genai, only for the deprecated Gemini node; this node needs Pillow, numpy and torch, all already in ComfyUI. No downloads, no API keys.
Where people get burned
Only .safetensors gets scanned. A folder of .ckpt files looks empty to this node, and GGUF quants are invisible to it - the classic "I have 20 models and it says none matched" moment.
A broken regex fails silently. An invalid pattern gets logged and ignored, so you get the unfiltered list and an index that means something else than you thought. If the sweep starts on the wrong model, check the pattern first.
Each run is a full disk load. Ten SDXL checkpoints means ten ~6.5GB reads. The node keeps memory sane by holding one model at a time; it doesn't make the sweep fast.
Keep one architecture per sweep. The loader guesses the model type from the weights and errors out on things it doesn't recognise - unet-only exports, odd files, checkpoints whose CLIP or VAE isn't actually inside them. SDXL merges against SDXL merges is the sweet spot.
The number in the box is the next checkpoint. index is a control_after_generate widget, same machinery as a KSampler seed, and it ticks after each run finishes. That's what makes a sweep work - but the value in the box has already moved past the model in the image you're looking at. After the sweep it sits at your count, so reloading the workflow won't re-run a finished comparison by accident.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| folder | STRING | . | Folder relative to models/checkpoints (or absolute) |
| index | INT | 00β9999 | Which checkpoint to load; errors past the end |
| include_patternopt | STRING | Regex to include (empty = all) | |
| exclude_patternopt | STRING | Regex to exclude (empty = none) |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| model | MODEL | β |
| clip | CLIP | β |
| vae | VAE | β |
| ckpt_name | STRING | β |
| label | STRING | β |
| index | INT | β |
| count | INT | β |