Save Image (Atomic)
The Save Image for people whose scripts watch the output folder
- images
Stock Save Image is fine - unless something else is watching your output folder. A Discord bot, a file harvester, a cron that picks up new PNGs the moment they appear, a script that forwards finished images somewhere. Those tools have a nasty habit of catching files mid-write: stock Save Image opens the target file and writes into it in place, so a watcher can grab it while it's zero bytes or half-written, and now your pipeline has a corrupt PNG it will forever try to read.
Save Image (Atomic) is the drop-in fix: same naming, same folder, same metadata - but the file is written to a temp sibling (.partial.<pid>) and moved into place with os.replace() only when the write is complete. The final filename appears on disk atomically. A watcher either sees nothing, or sees the finished file. Never a half-written one.
That's the entire idea, and if nothing harvests your output folder you genuinely gain nothing - this is a node for people running ComfyUI as a stage in a bigger automated pipeline, not a quality knob. It ships in the Model Phase Sync pack because the pack's whole philosophy is checkpointing and atomic handoff between phases: same discipline as its Save Latent (Atomic) and Save Conditioning nodes, which exist so downstream processes never act on truncated data.
How it works
Mechanically it's the core Save Image writer with the rename swapped in: it embeds the prompt and any extra_pnginfo as PNG metadata (unless you run with --disable-metadata), uses the same counter-based filenames (prefix_00001_.png, incrementing per batch), honors subfolders in filename_prefix, and compresses at level 4 - the core default. The only real difference is that the bytes land in a temp file first and os.replace() promotes it.
The inputs
Only two, and both are the same as stock:
images(required, IMAGE) - the batch of images to save.filename_prefix(required STRING, default"ComfyUI") - same convention as core Save Image: prefix and optional subfolder.outputs/out/puts files inout/.
No outputs - it's an output node (is_output_node: true), so ComfyUI always runs it and treats it as the end of a branch.
Install
This is one of five-ish nodes in Model Phase Sync. Via Manager search "Model Phase Sync", or:
comfy node install model-phase-sync
Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/josetseph/ComfyUI-ModelPhaseSync.git
Restart ComfyUI; it's under model_phase_sync. No Python dependencies, no downloads, MIT licensed.
Gotchas
- Don't expect different filenames than stock - if your watcher keys off the
_00001_pattern, it keeps working unchanged. - The atomic guarantee only covers the file appearing complete. If your watcher then reads it while it's complete, fine; if it watches for the filename before the bytes are there, it still works - that's the point.
- There's a deliberate reason it lives here rather than in a save-focused pack: it's meant to be the terminal node of a barrier workflow where each branch lands independently and a downstream process collects results as they finish.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| filename_prefix | STRING | ComfyUI | — |
Outputs (0)
No outputs