Optional Latent (obvpm)
For the latent that may or may not exist
- input
- latent
- present
Latents are the awkward citizens of a ComfyUI graph. They're the thing every sampler eats and produces, they're just a dict wrapping a tensor, and they're the one type where an unexpected None produces an error message so far from the cause that you spend ten minutes staring at the wrong node. So a gate that's typed specifically to LATENT isn't just pedantry - it's a place for the wiring mistake to stop.
What it does
The same three-part gate as the rest of the family. input is an optional LATENT; if it's connected, it comes straight back out of latent. If it isn't, on_empty chooses:
mute(the default) - anExecutionBlockergoes out and everything downstream is silently skipped.bypass-Nonegoes out, and a downstream node that declared its latent input optional reads it as unconnected.
The third piece is present, a BOOLEAN that's true when a latent is wired in and stays live even in mute mode. Drive a Lazy Switch with it and the graph can route itself: latent available → run the img2img path, no latent → run the text-to-image path.
Inputs and outputs
on_empty (mute/bypass, default mute), one optional LATENT input, outputs latent and present. That's the node.
The workflows this is for
Optional img2img. A loader or a latent-source node wired into the gate; take latent into the sampler when it's there, present into a switch otherwise. One file, both behaviours, no hand-bypassing.
Optional second pass. Hires-fix, a detail pass, or a low-denoise refinement you sometimes run and sometimes don't. Mute the gate and the whole second sampler is skipped; bypass it and a tolerant consumer copes.
Branching between two latent sources. Two samplers producing latents for the same decode, with the gate making one of them conditional. Latents are cheap to hand around - a reference to a tensor, not a copy - so putting a gate in the middle costs you nothing measurable.
The mechanism is identical across the whole gate family, and the source for all of them is one short base class; the only things that differ per type are the socket name and the declared type. That's the honest summary of why this node exists: it's Optional Image with LATENT written on it, and it exists so the type checker works for you.
What to expect when it goes wrong
Same two traps as its siblings, and they're worth repeating because they're the whole story with gates.
bypass pushes the problem downstream. None into a required latent input is still an error, just reported at the consumer. If your error message shows up three nodes later, that's what happened - you wanted the downstream node to handle absence and it doesn't.
mute stops the branch, not the work. Nodes upstream of input already ran. If the point was to save the sampling time of an unselected branch, mute was the wrong tool; that's the Lazy Switch's job.
And note the thing that isn't obvious if you're coming from the wildcard node: a LATENT socket will not accept an IMAGE. That's the feature. If you're genuinely unsure what's arriving, use Optional Any and lose the safety net on purpose.
Install
Manager, search comfyui-obvpm. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
Restart ComfyUI. Nothing else to install - no Python dependencies (the pack declares none deliberately, since everything it imports already comes with ComfyUI), no model downloads. Every node carries an (obvpm) suffix, so obvpm in the node search menu is the index.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| on_empty | COMBO | mute | What downstream sees when no input is connected: mute skips every downstream node, bypass outputs None. |
| inputopt | LATENT | The value to pass through. May be left unconnected. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| latent | LATENT | The input passed through. When the input is empty: blocked (mute) or None (bypass). |
| present | BOOLEAN | True when an input is connected. Stays live even in mute mode. |