invoke-ai/InvokeAI · error · ValueError

encode_caption_for_pid requires at least one caption.

Error message

encode_caption_for_pid requires at least one caption.

What it means

encode_caption_for_pid encodes a non-empty list of captions for the PiD decoder; the padded-token mask is essential to correct output, so encoding zero captions is rejected outright with ValueError.

Source

Thrown at invokeai/backend/pid/decode.py:598

    encoder: "object",  # Gemma2Model
    device: torch.device,
    dtype: torch.dtype = torch.bfloat16,
    chi_prompt: str = PID_CHI_PROMPT,
    model_max_length: int = PID_MODEL_MAX_LENGTH,
) -> tuple[Tensor, Tensor]:
    """Mirror of `PixelDiTModel._encode_text_raw`.

    Prepends the chi-prompt, tokenises with right-padding, runs Gemma's
    `model` (the transformer stack without the LM head), and selects
    ``[CLS] + last (model_max_length - 1)`` tokens to yield a fixed
    ``[B, model_max_length, 2304]`` embedding plus the matching attention
    mask. The mask is critical: PidNet's joint attention zeros padded text
    tokens out via this mask. Without it the decoder treats all ~300 slots
    (including the padding) as valid caption tokens and produces a
    washed-out average image.
    """
    if not captions:
        raise ValueError("encode_caption_for_pid requires at least one caption.")
    n_chi_tokens = len(tokenizer.encode(chi_prompt)) if chi_prompt else 0
    prompts = [chi_prompt + c for c in captions]
    max_len = (n_chi_tokens + model_max_length - 2) if chi_prompt else model_max_length
    # PiD was trained with right-padding (see PixelDiTModel._load_text_encoder
    # upstream). Gemma2's tokenizer defaults to "left" which would push the
    # BOS token away from index 0 and shove pads into the slice the decoder
    # consumes — yielding a garbled caption embedding. We toggle the value
    # for the duration of this call and restore it afterwards so we don't
    # poison the shared cached tokenizer.
    old_padding_side = getattr(tokenizer, "padding_side", "right")
    try:
        tokenizer.padding_side = "right"
        toks = tokenizer(
            prompts,
            max_length=max_len,
            padding="max_length",
            truncation=True,
            return_tensors="pt",

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Ensure at least one non-empty caption string is passed in the captions list
  2. Fix upstream prompt sanitization so it substitutes a default prompt instead of dropping all prompts
  3. Add a caller-side check: if not captions: use a fallback prompt

Example fix

// before
embs = encode_caption_for_pid(tok, te, captions=[])
// after
captions = captions or [""]
embs = encode_caption_for_pid(tok, te, captions=captions)
Defensive patterns

Strategy: validation

Validate before calling

if not captions:
    captions = [default_prompt]
embs = encode_caption_for_pid(tokenizer, text_encoder, captions=captions, chi_prompt=chi_prompt)

Type guard

def has_captions(captions) -> bool:
    return bool(captions) and all(isinstance(c, str) for c in captions)

Try / catch

try:
    embs = encode_caption_for_pid(tok, te, captions=captions, chi_prompt=chi)
except ValueError:
    embs = encode_caption_for_pid(tok, te, captions=[default_prompt], chi_prompt=chi)

Prevention

When it happens

Trigger: Calling encode_caption_for_pid(tokenizer, text_encoder, captions=[], ...) — an empty list (or empty container) of captions.

Common situations: A prompt-processing node upstream producing an empty prompt list after filtering blank prompts; a batched invoke where all prompts were dropped; a caller passing None coerced to an empty list.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/10a09d3d5ceec689. Report an issue: GitHub.