skills/creative/comfyui/references/template-integrity.md
Authored by @purzbeats — adapted from purzbeats/hermes-agent-comfyui-helper. Use this reference when converting workflows from the official
comfyui-workflow-templatespackage (editor format) into API format for submission via/api/prompt. The conversion has subtle gotchas that cause hard-to-diagnose validation errors if you don't follow these rules.
The official ComfyUI template package (comfyui-workflow-templates, currently
v0.9.69) is installed inside the ComfyUI venv at a path like:
<comfy-install>/.venv/lib/python3.*/site-packages/comfyui_workflow_templates_*/templates/
The exact path depends on how ComfyUI was installed (comfy-cli default, Comfy Desktop, manual venv, etc.). Find it once with:
comfy --workspace <ws> run-python -c "import comfyui_workflow_templates, pathlib; print(pathlib.Path(comfyui_workflow_templates.__file__).parent / 'templates')"
Templates ship in editor format — nodes / links arrays inside
data['definitions']['subgraphs'][0]. They must be converted to API
format (a node_id -> {class_type, inputs} mapping) before submission.
When a workflow submission fails, the server response looks like:
{
"node_errors": {
"238": {
"errors": [{
"message": "Required input is missing",
"details": "width",
"extra_info": { "input_name": "resize_type.width" }
}]
}
}
}
The extra_info.input_name field tells you EXACTLY what JSON key the server
wants. Use it literally. If it says "values.a" or "resize_type.width",
those are the actual key names in the JSON object. Do not "simplify" them to
flat names based on assumptions about what the field "should" be called.
Every regeneration from the template reintroduces the same bugs. Instead:
Most servers (local, Cloud) don't have a Reroute node type. When converting
a template:
target_id = the Reroute node ID.[source_node_id, source_slot].Real example — LTX 2.3 t2v template:
CheckpointLoaderSimple 236 slot 2.LTXVImgToVideoInplace (230), LTXVLatentUpsampler (253),
VAEDecodeTiled (251).vae: ["255", 0] with vae: ["236", 2].CheckpointLoaderSimple slot 2 = VAE (not slot 0 = MODEL).| ❌ Wrong | vae: ["236", 0] → MODELV mismatch input_type(VAE) |
| ✅ Correct | vae: ["236", 2] |
{
"class_type": "ComfyMathExpression",
"inputs": {
"expression": "a/2",
"values.a": ["257", 0]
}
}
values is a COMFY_AUTOGROW_V3 template.values.a, values.b, etc.{"values": {"a": ...}} or flatten to just "a".{
"class_type": "ResizeImageMaskNode",
"inputs": {
"input": ["276", 0],
"scale_method": "lanczos",
"resize_type": "scale dimensions",
"resize_type.width": 1920,
"resize_type.height": 1088,
"resize_type.crop": "center"
}
}
resize_type is a COMFY_DYNAMICCOMBO_V3.resize_type.width, resize_type.height, resize_type.crop.scale_method options: "nearest-exact", "bilinear", "area", "bicubic", "lanczos".resize_type.width to just "width".data['definitions']['subgraphs'][0].sg['links'] dict.widgets_values to input field names.SaveVideo terminal node if template uses only CreateVideo.| Element | Why |
|---|---|
| Node topology | Graph is designed for the specific model |
| Sigmas values | Tuned for the model/sampler combination |
| LoRA/distilled paths | Required for quality, even if they look unused |
| Model parameters (cfg, steps, shifts) | Model-specific |
| Conditioning chains (zero-out, crop guides) | Required for correct conditioning |
| Pass-through wiring | Don't remove nodes, bypass them |
The full LTX 2.3 T2V template (video_ltx2_3_t2v.json) runs without
modification on Comfy Cloud.
Confirmed working on Cloud (all custom nodes available):
ComfyMathExpression, ResizeImageMaskNode, ResizeImagesByLongerEdge,
PrimitiveInt, PrimitiveStringMultiline, PrimitiveBoolean, SaveVideo,
LTXVCropGuides, LTXVImgToVideoInplace, LTXVConcatAVLatent,
LTXVSeparateAVLatent, LTXVLatentUpsampler, LTXVAudioVAELoader,
LTXVAudioVAEDecode, LTXVEmptyLatentAudio, LTXVPreprocess,
LTXVConditioning, ManualSigmas, LTXAVTextEncoderLoader, plus all core
nodes.
Cloud vs Local for LTX 2.3 (768x512):
example.png placeholder works on Cloud for bypassed image-dependent paths.{"prompt": wf, "extra_data": {}} to /api/prompt.Cloud submission pitfalls:
/api/object_info/<node> returns 404 on free tier — can't query node
schemas remotely, but the workflow runs fine anyway. Always probe
object_info locally before building workflows./api/view returns 302 redirect to signed GCS URL — use
curl -s -L to follow and download. Python urllib fails with 401
(forwards auth headers to GCS CDN).COMFY_CLOUD_API_KEY is only in the terminal/bash env, not in the Python
sandbox. Use subprocess or terminal scripts for Cloud API calls.Generated ComfyUI videos often use yuv444p pixel format which does NOT work
on Discord. Re-encode with:
ffmpeg -y -i input.mp4 \
-c:v libx264 -profile:v main -preset medium -crf 13 -pix_fmt yuv420p \
-c:a aac -b:a 192k \
output_discord.mp4
Key settings:
-pix_fmt yuv420p — required for Discord, ComfyUI outputs yuv444p by default.-crf 13 — high quality without massive file size (default 23 is too lossy).-profile:v main — widely compatible.For multi-video crossfade stitching, chain xfade (video) and acrossfade
(audio):
ffmpeg -y -i a.mp4 -i b.mp4 -i c.mp4 \
-filter_complex "[0:v][1:v]xfade=transition=fade:duration=1:offset=3.04[v1];[v1][2:v]xfade=transition=fade:duration=1:offset=6.08[vout];[0:a][1:a]acrossfade=duration=1:c1=tri:c2=tri[a1];[a1][2:a]acrossfade=duration=1:c1=tri:c2=tri[aout]" \
-map "[vout]" -map "[aout]" \
-c:v libx264 -profile:v main -crf 13 -pix_fmt yuv420p \
-c:a aac -b:a 192k \
output.mp4
Offset for xfade #N = (N+1) × duration - N × overlap.