Nodes/ComfyUI Universal Extractor/Universal Prompt Snapshot
ComfyUI Node

Universal Prompt Snapshot

Your PNG's Prompt Is Not The Prompt You Ran. Snapshot The Real One.

By Tera-Dark·Created 5 months ago·Updated a day ago· 3
Universal Prompt Snapshot
    • text
    ◄text—►
    ◄role▾►

    Anyone who leans on wildcards, artist-string randomizers, or any node that composes a prompt at runtime has hit this: you open a PNG months later and the embedded prompt is not the prompt that made the picture. It's the config - the file name, the field path, the seed - because SaveImage dumps the execution graph, and the graph records what you fed the randomizer, not what the randomizer handed to CLIP. The image looks great and is, functionally, undocumented. That complaint is old and constant in the community, and it's exactly the hole Universal Prompt Snapshot plugs.

    What it actually does

    It's a one-line trick wrapped in a node: it takes a string, hands the identical string back out, and quietly writes the text into the run's extra_pnginfo dict on the way through. That dict is ComfyUI's per-execution scratch metadata - the thing SaveImage receives and serializes. Every key in it becomes its own PNG text chunk, so the node's entry lands in your PNG under the literal chunk name universal_prompt_snapshots, keyed by the snapshot node's own id, storing text, role, node_id and a version.

    Two consequences fall out of that. Because it's a pass-through, you drop it inline and nothing else notices: randomizer → Universal Prompt Snapshot → CLIPTextEncode → sampler. And the resolved string is now in the file, so the pack's own Gallery workbench (or anything that reads PNG chunks) can report the prompt that actually ran instead of shrugging. The Gallery's recipe extractor looks up this node's id when resolving a sampler's positive and negative text, and only trusts this node plus ComfyUI's literal string primitives - it refuses to re-run a randomizer or guess a custom node's output. If you want that "prompt resolved: yes" behaviour instead of an incomplete recipe, this node is the switch.

    The inputs and output that matter

    Per the node's schema there are exactly two inputs, and one of them is the one you'll think about.

    text is a forced input - a wire-only socket, no typing into it. That's the point: the value has to come from something upstream, otherwise there's nothing worth capturing. Wire your randomizer, your string concat, your wildcard node here.

    role is a three-way dropdown: positive, negative, notes. The author's own guidance is literal - pick positive for the positive branch, negative for the negative one. Worth knowing: the Gallery's recipe lookup matches on node id, not on role, so a wrong label won't break extraction; it exists so the chunk is self-describing to humans and other tools. Label it honestly anyway - future you is the reader.

    The single output is text, type STRING - the same string, unchanged. It goes into whatever consumes the prompt: a CLIPTextEncode, a concat node, another snapshot if you like racking them up. Use two instances in one graph (one per branch) and each gets its own entry keyed by its own id.

    There's no image output, no batch size, no seed widget. It's a tap.

    Install

    Easiest is ComfyUI Manager: search Universal Extractor and install it. Manually:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Tera-Dark/ComfyUI-Universal-Extractor.git
    pip install -r ComfyUI-Universal-Extractor/requirements.txt
    

    Then restart ComfyUI. It's also on the registry as comfy node registry-install tera-universal-extractor. The dependency list is short and light - Pillow>=10.0.0 and Send2Trash - no models, no extra CUDA anything. Needs ComfyUI 0.3.0+ and Python 3.10+.

    One version note: the node is new in 1.3.0. If you have the pack but not the node, update and restart - the README's own advice is that stale node definitions linger in ComfyUI and the browser until you restart both.

    Where people get burned

    • Saving metadata is off. Launch ComfyUI with --disable-metadata (or flip the equivalent server setting) and the whole write block is skipped. You'll get a node that does nothing visible and a folder of PNGs with no prompt data. That's not a bug.
    • Leaving the output dangling. The node isn't an output node, so if nothing downstream pulls on its text socket, ComfyUI simply doesn't execute it. There's no capture without a consumer. It works because it's inline.
    • Non-PNG saves. The snapshots ride in PNG tEXt chunks. JPEG and WebP have no equivalent, and any host that re-encodes your PNG (Reddit's preview pipeline being the classic) drops it too.
    • Multi-sampler graphs. The Gallery won't guess which branch produced which image when several samplers or complex conditioning are in play; it reports the summary as incomplete rather than inventing an answer. The snapshot only helps where the sampler's prompt path is unambiguous.
    • Cached upstream, fresh snapshot. The node declares itself always-dirty (IS_CHANGED returns NaN) so the capture re-fires every run while the randomizer behind it can stay cached. That's intentional - but the string you get is whatever upstream emitted this run, and the node won't tell you where it came from.

    Last thing: you're now writing a clean, machine-readable copy of your resolved prompt into every PNG. If that string is your secret sauce, this does the opposite of what you want.

    CategoryUniversal Tools

    Inputs (2)

    NameTypeDefaultDescription
    textSTRING—
    roleCOMBO3 options: positive, negative, notes

    Outputs (1)

    NameTypeDescription
    textSTRING—