Zura Save Video Latent Data
Park an encoded video latent on disk and sample it later
- samples
- cache_key
- cache_digest
ComfyUI's caching is session-scoped and all-or-nothing. Restart the app and a ten-minute preparation pass is gone; keep the app open and you're still holding the encoded latent in RAM whether you're using it or not. This node is the escape hatch: write the prepared video latent to disk, then rebuild it later for free.
Why bother
Video prep is expensive and iterative work isn't. Decode this, mask that, detect the performer, track them, encode. Five minutes of GPU for something you will not change again - and then you spend an hour sweeping seeds and steps. Splitting the graph so that preparation is a saved artifact and sampling is the fast loop is the difference between iterating and waiting.
The author's framing is precise about scope: "Safely caches the prepared video latent on disk so another native graph section can sample it later. No models run here." It's an output node - in the sense of "has a side effect", not "preview" - and it stamps the cache with a schema and version. The pair of nodes is data-only persistence for native video latent dictionaries, and they do not load models or infer anything.
How it works
The cache lives at output/zura/latent_data/<cache_key>.safetensors. Your cache_key has to be exactly 64 lowercase hex characters - generate one, don't improvise:
python -c "import uuid; print(uuid.uuid4().hex)"
The latent dict is validated before anything touches disk: it may contain samples and an optional noise_mask and nothing else, both must be 5-dimensional floating-point strided tensors with finite values, the mask must broadcast to the samples and sit in 0–1, and the whole payload is capped at 256 MiB plus 64 KiB of safetensors header.
Then it snapshots to CPU, serializes with safetensors, and publishes atomically - a temp file, a lock file, and an os.link publish so a concurrent run can't half-write the cache. If the key already exists, it compares the existing content tensor by tensor and byte for byte rather than assuming. Identical content? Fine, it returns the existing digest. Different content under the same key? Hard error.
It returns the key back (so you can wire it into the load node instead of retyping) and the file's SHA-256 digest, which you should absolutely wire into Zura Load Video Latent Data's optional cache_digest input.
Inputs and outputs
samples-LATENT, the prepared video latent.cache_key-STRING, default empty. Your 64-hex key.cache_key(output) -STRING, echoed back. Into the load node.cache_digest(output) -STRING, the SHA-256 of the written file. Into the load node's optional digest slot.
Install
cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt
Restart. Nothing extra - safetensors ships with ComfyUI. The cache directory is created under your ComfyUI output folder, and every path component is resolved and re-checked to stay inside it, so a symlinked output/zura gets rejected rather than silently writing somewhere else.
Common issues
"Cache key must be exactly 64 lowercase hexadecimal characters." Uppercase, wrong length, or a friendly name. Generate one.
"This cache key already contains different video latent data. Use a new cache key." You changed something upstream - a different clip, mask or resolution - and reused the key. Two options, and only one is safe: keep the old cache, or mint a new key. Editing a latent in place while a key that means "the old one" still exists on disk is how you end up debugging a render against the wrong preparation.
"Video latent has unsupported metadata (…)." The latent carries extra keys. Only samples and noise_mask survive the round trip, and the node refuses to silently drop the rest - a nested audio-video latent with extras won't cache here. That's the H3 case in particular: this is for plain video latents.
"Video latent data exceeds the 256 MiB cache limit." A big latent. A 124-frame 704×1280 H3 video latent is comfortably inside it; a long clip at high resolution is not. Trim, or lower the working resolution.
Nothing to load later. If the file's there and the load node still complains, check the digest you wired. That check exists to catch the case where the file was replaced between the save and the load - a stale digest is a real signal, not noise.
It runs every time. It's an output node, so yes: every Run rewrites (or verifies) the cache. The write path is idempotent, so the cost is one comparison, not one re-encode.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| samples | LATENT | — | |
| cache_key | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| cache_key | STRING | — |
| cache_digest | STRING | — |