Subgraph flatten queues wrong widget values (off-by-one) - 113 shipping templates carry the defect, some corrupt silently (fix included)
- Dominant language
- Python
- Stars
- 133k
- Forks
- 15.7k
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 158
Description
## Expected Behavior
Queueing a workflow containing a subgraph produces the same prompt values the UI displays.
## Actual Behavior
If a subgraph definition's inner node has a widget-input still linked to the subgraph input boundary (legacy promoted-widget style: the input entry carries a `widget` key and its `link` id appears in `definitions.subgraphs[].inputs[].linkIds`), and that boundary input is dangling (no parent-side instance feeds it a link, instance `widgets_values` empty), then flattening (`graphToPrompt`) falls back to positional `widgets_values` mapping and does not account for the `control_after_generate` pseudo-widget. Every widget value after the missed slot shifts by one:
- `steps` receives the seed value: `Value N bigger than max of 10000: steps`
- `sampler_name` receives a number: `'1' is not in list`
The UI renders all widget values (seed, steps, cfg, sampler_name, …) correctly. Only the generated prompt is wrong. So the defect gets reported against whatever node fails validation instead of the serialization bug. This is the actual root cause behind #13627 (analysis posted there: https://github.com/Comfy-Org/ComfyUI/issues/13627#issuecomment-5014885724) and likely behind other "promoted widgets wrong after upgrade" reports (#13029, #13326, #13157). Filing standalone because that issue's title makes it look z-image-specific.
## Steps to Reproduce
On the current core pin (frontend 1.45.20), with an official template:
1. Load `utility_seedvr2_3b_int8_upscale_video.json` from Comfy-Org/workflow_templates `main`.
2. The subgraph's KSampler shows sensible values in the UI (euler / simple / denoise 1.0).
3. Queue, or run `(await app.graphToPrompt()).output["66:54"].inputs` in the console.
4. Validation fails on that KSampler: `sampler_name` = `1`, `scheduler` = `1`, `denoise` = `"euler"`. The values shifted one slot.
Some corruptions run silently: `image_boogu_image_0_1_edit.json` (shipping templates package 0.1.6) flattens `noise_seed=true`, which validates as INT and runs with seed 1 instead of 22. Wrong output, no error. `video_capybara_v0_1_video_edit.json` similarly runs with `batch_index=121`.
## Scope
The trigger is the serialized subgraph structure, not any node or model family. Subgraphs freshly created with Convert to Subgraph serialize these inputs with `link: null` and are fine. The defect affects definitions created or imported from older frontends.
This defective serialization ships in official templates at scale (static scan 2026-07-26): 175 of 581 templates on Comfy-Org/workflow_templates `main`, and 113 of 471 in the shipping `comfyui-workflow-templates-json` 0.1.6 package, including the first-run onboarding template (`01_get_started_text_to_image.json`). Carrying it does not always corrupt. Which templates corrupt depends on the frontend version. Confirmed by prompt-diff on 1.45.20: `utility_seedvr2_3b_int8_upscale_video.json`, `templates-6-key-frames.json`, `video_capybara_v0_1_video_edit.json`, `gsl_creator_2.json`, `image_boogu_image_0_1_edit.json` / `_int8`. On 1.46.15: `image_z_image.json` (seed lands in `steps`, node `76:69` - that instance is #13627). Full scan listings and per-node diffs available on request.
Static detection without queueing: flag any inner-node input that (1) has a `widget` key, (2) has `link` in a boundary input's `linkIds`, and (3) that boundary input is dangling on every instance. Boundary widgets actually fed by a parent link resolve fine and must not be flagged.
## Versions
Confirmed on frontend 1.45.20 (core 0.27.0 pin, 2026-07-26) and 1.46.15 (z-image case, 2026-07-19). On ComfyUI_frontend `main` (2026-07-26), `SubgraphNode._applyPromotedWidgetValues` (src/lib/litegraph/src/subgraph/SubgraphNode.ts) still consumes `widgets_values` positionally with no `control_after_generate` handling, so the bug remains reachable on current code.
## Suggested fixes (increasing scope)
a. Skip `control_after_generate` in the positional fallback of the flatten path.
b. Treat dangling boundary-linked widget-inputs as `link: null` at load time (de-promote), matching what freshly created subgraphs serialize. Verified locally via before/after `graphToPrompt` inspection: this fixes affected workflows without restructuring the subgraph.
c. Structural end state: named widget values per Comfy-Org/ComfyUI_frontend#10392. Update: that PR merged 2026-07-30 behind the disabled-by-default `Comfy.Workflow.NamedValuesRestore` setting. The positional fallback is still the default path, so (a) or (b) is still needed.
Separately from the code fix: re-export the affected official templates from a current frontend, confirmed-corrupting ones first, so new users stop hitting this out of the box.
Scanner/fixer scripts available on request (the fixer validates every candidate against `/object_info` schema and flags rather than fixes anything it can't prove value-preserving).
Contributor guide
Research direction
Start at graphToPrompt and SubgraphNode._applyPromotedWidgetValues in src/lib/litegraph/src/subgraph/SubgraphNode.ts, then reproduce with utility_seedvr2_3b_int8_upscale_video.json. Compare the generated prompt inputs with the values shown in the UI, focusing on dangling boundary-linked widgets and control_after_generate. Done means affected subgraphs retain the correct widget order and no values shift during queueing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100