UI→API conversion drops a record-valued widget (ImageCropV2.crop_region) and runs the node with its default

オープン
#1,011 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@allenflux がすでに取り組んでいます。

2026年10月16日 から。

  • #1016 @allenflux による — オープン

評価

難易度
3/5
見積もり時間
半日
初心者へのやさしさ
66/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python
領域
cli, tooling

調査の方向性

Start in workflow_to_api.py at _collect_widget_inputs (around L1671), which hands the whole node to _absorb_dict_widget_values (around L1714) whenever any widget value is a dict. Run the repro from the issue against convert_ui_to_api and confirm crop_region comes back as the schema default. Done when the record in widgets_values lands on its socketless widget and the Power Lora path still works.

索引モデルが issue の本文から書いたものです。

説明

comfy-cli 1.22.0 (comfy-mcp 0.10.0 on top of it), ComfyUI 0.38.x, Windows 11. Reproduced on main (workflow_to_api.py, _collect_widget_inputs at L1671 and _absorb_dict_widget_values at L1714).

What happens

A node whose widget stores a record in widgets_values loses that value in the UI→API conversion. ImageCropV2 stores its crop_region (type BOUNDING_BOX, socketless: true) as {"x", "y", "width", "height"}; the converted prompt carries the schema default {"x": 0, "y": 0, "width": 512, "height": 512} instead. The job runs, so nothing warns: in the bundled 3d_pixal3d_multi_views template the four crops all become the same 512×512 corner of the input picture, the four "views" fed to Pixal3DMultiViewConditioning are identical, and the generated mesh is a radially symmetric object instead of the pictured one. The same workflow queued from the frontend gives the right prompt.

Repro (self-contained: the two schemas as GET /object_info returns them on ComfyUI 0.38, the node as the frontend saves it)

from comfy_cli.workflow_to_api import convert_ui_to_api
object_info = {
  "LoadImage": {"input": {"required": {"image": [["house.png"], {"image_upload": True}]}}, "input_order": {"required": ["image"]},
                "output": ["IMAGE", "MASK"], "output_name": ["IMAGE", "MASK"]},
  "ImageCropV2": {"input": {"required": {"image": ["IMAGE", {}],
                   "crop_region": ["BOUNDING_BOX", {"default": {"x": 0, "y": 0, "width": 512, "height": 512}, "socketless": True, "component": "ImageCrop"}]}},
                  "input_order": {"required": ["image", "crop_region"]}, "output": ["IMAGE"], "output_name": ["IMAGE"]},
}
wf = {"nodes": [
  {"id": 364, "type": "LoadImage", "widgets_values": ["house.png", "image"], "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [1]}]},
  {"id": 338, "type": "ImageCropV2",
   "inputs": [{"name": "image", "type": "IMAGE", "link": 1},
              {"name": "crop_region", "type": "BOUNDING_BOX", "widget": {"name": "crop_region"}, "link": None}],
   "widgets_values": [{"x": 0, "y": 0, "width": 1669, "height": 2688}, 0, 0, 512, 512]}],
  "links": [[1, 364, 0, 338, 0, "IMAGE"]], "version": 0.4}
print(convert_ui_to_api(wf, object_info)["338"]["inputs"]["crop_region"])

Observed: {'x': 0, 'y': 0, 'width': 512, 'height': 512}. Expected: {'x': 0, 'y': 0, 'width': 1669, 'height': 2688}. The trailing 0, 0, 512, 512 are the Custom-ratio sub-widgets the frontend also serializes; with them removed the result is the same.

Why

_collect_widget_inputs sends the whole node through _absorb_dict_widget_values as soon as any value in the list is a dict (L1671). That function only understands Power Lora entries (a dict with type or lora); every other record is skipped, the input is never set, and _collect_default_inputs fills it from the schema. _is_widget_input does give crop_region a slot (it is socketless), so this is not the missing-slot case of #893: the slot exists, the value is thrown away by the dict shortcut.

Same shape for any socketless input whose frontend widget serializes a record: CreateBoundingBoxes.last_incoming, CropByBBoxes.bboxes, SAM3_Detect.bboxes, DrawBBoxes.bboxes, VideoTrim.trim, VideoCrop.crop (VIDEO_EDIT), CurveEditor.curve, Painter.mask, ImageCompositor.compositor, ImageCompare.compare_view, ResolutionSelector.preview on this install.

Suggestion

Take the Power Lora shortcut only when the dicts actually look like Power Lora entries (type/lora keys), and run the normal schema walk otherwise, letting a record land on its widget like a scalar does. Pairing by name (#873) would also solve it.

Workaround we use

Rewriting the node's widgets_values as a dict ({"crop_region": {...}}) before conversion: the converter already honours that form and the prompt comes out as saved.

主要言語
Python
スター
1k
フォーク
176
平均マージ
1日 11時間
マージ済み PR(30日)
87

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Comfy-Org/comfy-cli のほかの issue

Comfy-Org/comfy-cli の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。