unslothai/unsloth · error · ValueError

A prompts list is supported for plain text-to-image only; th

Error message

A prompts list is supported for plain text-to-image only; the {workflow} workflow takes one prompt per call (seed lists still work).

What it means

A prompt list (batching multiple prompts in one call) only works for the plain txt2img workflow. Conditioned workflows (inpaint, img2img, edit, reference, controlnet, upscale) take exactly one prompt per call because they carry one conditioning image; accepting a list would silently broadcast the same image against every prompt. uniform_prompt(jobs) returning None means the prompts were not a single uniform value, so with any non-txt2img workflow the call is rejected.

Source

Thrown at studio/backend/core/inference/diffusion.py:5457

                        src = decode_b64_image(cn_image_b64, mode = "RGB")
                        control_pil = diffusion_controlnet.preprocess_control(src, cn_type).resize(
                            (width, height), Image.LANCZOS
                        )
                        try:
                            resolved_cn = diffusion_controlnet.resolve_controlnet(
                                cn_id, family = state.family.name
                            )
                        except FileNotFoundError as exc:
                            # An unknown CN id -> 400, not 500 (the route maps ValueError).
                            raise ValueError(str(exc)) from exc
                        pipe = self._controlnet_pipe(state, resolved_cn, cancel)
                        workflow = "controlnet"
                        cn_scale, cn_gstart, cn_gend = cn_strength, cn_gs, cn_ge
                        # Flux Union CN selects its head by an integer control_mode; map the type.
                        cn_mode = diffusion_controlnet.union_control_mode(cn_id, cn_type)
                # A prompt LIST batches plain text-to-image only: conditioned workflows take one image per call and a silent broadcast would pair every prompt with it.
                if uniform_prompt(jobs) is None and workflow != "txt2img":
                    raise ValueError(
                        "A prompts list is supported for plain text-to-image only; the "
                        f"{workflow} workflow takes one prompt per call (seed lists still work)."
                    )
                # Snap odd-sized inputs (and the mask) to a multiple of 16 where the OUTPUT size comes from the input image.
                if init_pil is not None and workflow in ("img2img", "inpaint", "edit"):
                    # img2img/inpaint take output size from the upload, so bound the longest side to 2048 (a phone photo would OOM).
                    if workflow == "img2img":
                        # ...and bound Transform by the REQUESTED size too, so the Resolution
                        # control caps the output instead of being inert. img2img only: an
                        # inpaint payload is the canvas the mask was painted against (Extend
                        # sends a deliberately ENLARGED one), so shrinking it would break them.
                        init_pil = _fit_within(init_pil, min(2048, width), min(2048, height))
                    elif workflow == "inpaint":
                        init_pil = _clamp_max_side(init_pil, 2048)
                    init_pil = _snap_to_multiple(init_pil, 16)
                    if mask_pil is not None and mask_pil.size != init_pil.size:
                        from PIL import Image as _PILImage
                        mask_pil = mask_pil.resize(init_pil.size, _PILImage.NEAREST)

View on GitHub (pinned to 203007d190)

Solutions

  1. Split the call: issue one request per prompt for the conditioned workflow (loop client-side).
  2. Keep the prompt list only for plain txt2img (no init_image/mask/controlnet/reference).
  3. Remember seeds lists still work with a single prompt in conditioned workflows — only the prompts list is restricted.

Example fix

# before
engine.generate(prompt=["a cat", "a dog"], init_image=img)  # ValueError
# after
for p in ["a cat", "a dog"]:
    engine.generate(prompt=p, init_image=img, seed=base_seed + i)
Defensive patterns

Strategy: validation

Validate before calling

def is_txt2img_request(payload: dict) -> bool:
    return not (payload.get("init_image") or payload.get("mask_image") or payload.get("controlnet"))

if isinstance(payload.get("prompt"), list) and not is_txt2img_request(payload):
    raise UserError("Split the prompt list into one call per prompt for this workflow.")

Type guard

def is_prompt_list(v) -> bool:
    return isinstance(v, list)

Try / catch

try:
    out = engine.generate(prompt=prompts, init_image=img)
except ValueError as e:
    if "prompts list is supported for plain text-to-image only" in str(e):
        out = [engine.generate(prompt=p, init_image=img) for p in prompts]
    else:
        raise

Prevention

When it happens

Trigger: Calling generate with a list of prompts (non-uniform) together with init_image, mask_image, controlnet, or any other feature that selects a workflow other than txt2img. Note: seed lists remain allowed in conditioned workflows.

Common situations: Reusing a batch-generation payload (prompts: [...]) after attaching an init image for img2img; building a UI 'generate variations' feature that batches prompts while a reference/ControlNet image is active.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/3aaadfce108748e6. Report an issue: GitHub.