EditUtils: QwenImage 2.1 Config Preparer lrzjason
One Ticket Per Reference Image
- image
- configs
- mask
- configs
- config
A node that writes tickets instead of tensors
ComfyUI-EditUtils doesn't put its settings on the encode node. It builds a small list of dictionaries - one per image - and the encode node reads that list. QwenImage21ConfigPreparer_EditUtils writes exactly one entry into that list: this image, at this size, is a reference (or isn't), is the main image whose latent you sample from (or isn't), goes to the vision tower (or doesn't), and sits at this spot on the canvas.
On its own it computes nothing you can see. Chain three of them and you've described three references; chain eight and you've described eight - which is the real reason this node exists, since the pack's one-piece QwenImage21EditTextEncode_EditUtils stops at three images. The chain path has no cap.
Context, briefly: Qwen-Image-Edit is the model that made a whole adapter stack unnecessary - reference conditioning arrives as an image plus a plain-language instruction, no mask, no ControlNet, no IP-Adapter. 2.1 keeps that shape but changes the plumbing: prompt and references are encoded together by a Qwen3-VL text encoder, and each reference latent gets spliced into the text sequence at the encoder's vision slots. That's what the fields below are serving.
The mechanism the fields are serving
The encode node downstream does the work: it scales each image toward ref_longest_edge, snaps it to a multiple of 32 pixels, pads it to the VAE grid, encodes it, and hangs the resulting latents on the conditioning under reference_latents (plus the rope offsets under reference_rope_offsets). Qwen-Image 2.1's VAE is 64-channel with 16x spatial downscale, so 32 pixels is 16 × 2 - one vision slot maps onto a 2×2 group of latent tokens. Get that alignment wrong and the vision tower and the diffusion model are talking about different pixels.
That's why ref_longest_edge steps in 32s. Don't fight it.
The fields a beginner actually touches
image- the reference (or the main image; same input either way).configs- the chain. Leave it empty on the first node, wire the previous node'sconfigsoutput into it for every one after.ref_main_image- the image whose padded latent becomes the sampling start latent. Exactly one node in the chain should have it on.ref_longest_edge- 1024 to start. Bigger refs aren't automatically better; you're paying tokens for them.to_vl- the author's tooltip says keep it enabled for trained behaviour, and the source agrees: with it off you're splicing a reference where the model was never trained to see one.
The rest are for when something is wrong: ref_crop (pad/center/disabled), ref_upscale (lanczos by default), ref_resize_mode (longest_edge matches ref_longest_edge to the long side; area matches total pixels to its square - a portrait and a landscape then cost the same), mask, and rope_x_offset / rope_y_offset, which only do anything if you also put QwenImage21EditApply_EditUtils in the graph.
Outputs
Two, and they're the same information: configs is the whole accumulated list - wire it to the next preparer or into the encode node's configs input. config is just this image's own dict, useful when you want to inspect it or hand a single entry to another node.
Install
ComfyUI Manager, search ComfyUI-EditUtils; or:
cd ComfyUI/custom_nodes
git clone https://github.com/lrzjason/ComfyUI-EditUtils
Restart ComfyUI. There's no requirements.txt here - the pack is plain Python over ComfyUI, so nothing extra gets pip-installed. The real dependency is your ComfyUI build: it needs upstream Qwen-Image 2.1 (qwen_image21) support. On an older build the model won't load at all. The example workflow loads qwen_image_2.1_int8_convrot.safetensors through UNETLoader, qwen3vl_8b_fp8_scaled.safetensors through CLIPLoader with type qwen_image, and qwen_image_2.1_vae_bf16.safetensors through VAELoader.
Where people get burned
to_vl=True together with to_ref=False looks like a sensible combo and isn't: the encoder forces to_ref back on and prints a note, because a vision slot with no latent behind it breaks the alignment. Set to_vl off if you want that image excluded from the references.
Two main images is a silent no-op - the second one's ref_main_image is forced to False, and if you set none at all the first image gets picked for you with a console note. Same shape of gotcha with mask: if its height and width don't match the image, it gets dropped and the run continues unmasked.
Finally, all-zero rope offsets are the native path, not a bug. If you wired the offsets expecting them to do something and nothing moved, check that the Edit Apply patch is actually in the graph.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| configsopt | LIST | Configs list | |
| to_refopt | BOOLEAN | true | Add image to reference latent |
| ref_main_imageopt | BOOLEAN | true | Set image as main image which would return the latent as output. |
| ref_longest_edgeopt | INT | 102432–4096 | Reference target size. Snapped to 32-pixel multiples (vision slot <-> 2x2 latent group alignment). |
| ref_cropopt | COMBO | pad | Crop method for reference image |
| ref_upscaleopt | COMBO | lanczos | Upscale method for reference image |
| to_vlopt | BOOLEAN | true | Feed the resized reference to the Qwen3-VL text encoder and splice its latent at the vision slot. Keep enabled for trained behavior. |
| maskopt | MASK | — | |
| ref_resize_modeopt | COMBO | longest_edge | longest_edge: scale so the longest dimension equals ref_longest_edge. area: scale so total pixels equals ref_longest_edge squared. |
| rope_x_offsetopt | INT | 00–4096 | ROPE horizontal position offset in pixels (32-aligned). Shifts reference rightward on the canvas. Requires QwenImage21EditApply_EditUtils. |
| rope_y_offsetopt | INT | 00–4096 | ROPE vertical position offset in pixels (32-aligned). Shifts reference downward on the canvas. Requires QwenImage21EditApply_EditUtils. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| configs | LIST | — |
| config | ANY | — |