Comfy-Org/ComfyUI · error · ValueError

Number of texts ({len(texts)}) does not match number of imag

Error message

Number of texts ({len(texts)}) does not match number of images ({num_images}). Text list should have length {num_images}, 1, or 0.

What it means

Thrown by the image+text dataset-encoding node when the text list length is neither 0/None (treated as unconditional), nor 1 (broadcast to all images), nor exactly the number of images. The node intentionally supports only these three shapes before it VAE-encodes the images.

Source

Thrown at comfy_extras/nodes_dataset.py:1901

    @classmethod
    def execute(cls, images, vae, clip, texts=None):
        # Extract scalars (vae and clip are single values wrapped in lists)
        vae = vae[0]
        clip = clip[0]

        # Handle text list
        num_images = len(images)

        if texts is None or len(texts) == 0:
            # Treat as [""] for unconditional training
            texts = [""]

        if len(texts) == 1 and num_images > 1:
            # Repeat single text for all images
            texts = texts * num_images
        elif len(texts) != num_images:
            raise ValueError(
                f"Number of texts ({len(texts)}) does not match number of images ({num_images}). "
                f"Text list should have length {num_images}, 1, or 0."
            )

        # Encode images with VAE
        logging.info(f"Encoding {num_images} images with VAE...")
        latents_list = []  # list[{"samples": tensor}]
        for img_tensor in images:
            # img_tensor is [1, H, W, 3]
            latent_tensor = vae.encode(img_tensor[:, :, :, :3])
            latents_list.append({"samples": latent_tensor})

        # Encode texts with CLIP
        logging.info(f"Encoding {len(texts)} texts with CLIP...")
        conditioning_list = []  # list[list[cond]]
        for text in texts:
            if text == "":
                cond = clip.encode_from_tokens_scheduled(clip.tokenize(""))

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Make len(texts) equal len(images), or pass exactly one caption to broadcast, or an empty list/None for unconditional.
  2. Rebuild the caption list from the same filtered image list (zip images and caption files together when loading).
  3. Print both lengths before the node to find which side drifted.

Example fix

# before
texts = load_captions(folder)          # 5 entries
images = load_images(folder)            # 7 after filtering
# after
images = load_images(folder)
texts = [captions[img.stem] for img in images]  # guaranteed same length
Defensive patterns

Strategy: validation

Validate before calling

n = len(images)
if texts is None or len(texts) == 0:
    texts = ['']            # unconditional
elif len(texts) == 1 and n > 1:
    texts = texts * n       # broadcast
assert len(texts) == n, f'texts={len(texts)} but images={n}'

Type guard

def texts_match_images(texts: list[str] | None, n: int) -> bool:
    return texts is None or len(texts) in (0, 1, n)

Prevention

When it happens

Trigger: Passing e.g. 5 captions for 7 images, or 7 captions for 5 images, into the texts input. Also passing a list-of-lists or nested structure that changes len(texts).

Common situations: Caption files (txt sidecars, CSV columns) that don't align 1:1 with images after filtering; manually curated caption lists where entries were added or deleted; image lists modified by resolution filtering after captions were loaded.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/1708eb5a8e628608. Report an issue: GitHub.