{"record":{"id":"10a09d3d5ceec689","repo":"invoke-ai/InvokeAI","slug":"encode-caption-for-pid-requires-at-least-one-capti","errorCode":null,"errorMessage":"encode_caption_for_pid requires at least one caption.","messagePattern":"encode_caption_for_pid requires at least one caption\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/pid/decode.py","lineNumber":598,"sourceCode":"    encoder: \"object\",  # Gemma2Model\n    device: torch.device,\n    dtype: torch.dtype = torch.bfloat16,\n    chi_prompt: str = PID_CHI_PROMPT,\n    model_max_length: int = PID_MODEL_MAX_LENGTH,\n) -> tuple[Tensor, Tensor]:\n    \"\"\"Mirror of `PixelDiTModel._encode_text_raw`.\n\n    Prepends the chi-prompt, tokenises with right-padding, runs Gemma's\n    `model` (the transformer stack without the LM head), and selects\n    ``[CLS] + last (model_max_length - 1)`` tokens to yield a fixed\n    ``[B, model_max_length, 2304]`` embedding plus the matching attention\n    mask. The mask is critical: PidNet's joint attention zeros padded text\n    tokens out via this mask. Without it the decoder treats all ~300 slots\n    (including the padding) as valid caption tokens and produces a\n    washed-out average image.\n    \"\"\"\n    if not captions:\n        raise ValueError(\"encode_caption_for_pid requires at least one caption.\")\n    n_chi_tokens = len(tokenizer.encode(chi_prompt)) if chi_prompt else 0\n    prompts = [chi_prompt + c for c in captions]\n    max_len = (n_chi_tokens + model_max_length - 2) if chi_prompt else model_max_length\n    # PiD was trained with right-padding (see PixelDiTModel._load_text_encoder\n    # upstream). Gemma2's tokenizer defaults to \"left\" which would push the\n    # BOS token away from index 0 and shove pads into the slice the decoder\n    # consumes — yielding a garbled caption embedding. We toggle the value\n    # for the duration of this call and restore it afterwards so we don't\n    # poison the shared cached tokenizer.\n    old_padding_side = getattr(tokenizer, \"padding_side\", \"right\")\n    try:\n        tokenizer.padding_side = \"right\"\n        toks = tokenizer(\n            prompts,\n            max_length=max_len,\n            padding=\"max_length\",\n            truncation=True,\n            return_tensors=\"pt\",","sourceCodeStart":580,"sourceCodeEnd":616,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/pid/decode.py#L580-L616","documentation":"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.","triggerScenarios":"Calling encode_caption_for_pid(tokenizer, text_encoder, captions=[], ...) — an empty list (or empty container) of captions.","commonSituations":"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.","solutions":["Ensure at least one non-empty caption string is passed in the captions list","Fix upstream prompt sanitization so it substitutes a default prompt instead of dropping all prompts","Add a caller-side check: if not captions: use a fallback prompt"],"exampleFix":"// before\nembs = encode_caption_for_pid(tok, te, captions=[])\n// after\ncaptions = captions or [\"\"]\nembs = encode_caption_for_pid(tok, te, captions=captions)","handlingStrategy":"validation","validationCode":"if not captions:\n    captions = [default_prompt]\nembs = encode_caption_for_pid(tokenizer, text_encoder, captions=captions, chi_prompt=chi_prompt)","typeGuard":"def has_captions(captions) -> bool:\n    return bool(captions) and all(isinstance(c, str) for c in captions)","tryCatchPattern":"try:\n    embs = encode_caption_for_pid(tok, te, captions=captions, chi_prompt=chi)\nexcept ValueError:\n    embs = encode_caption_for_pid(tok, te, captions=[default_prompt], chi_prompt=chi)","preventionTips":["Never filter prompt lists down to empty — substitute a default","Sanitize upstream prompt nodes to always emit at least one string","Log dropped prompts so silent empty-list cases surface early"],"tags":["valueerror","validation","empty-input"],"backgroundTag":"empty-input-validation","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}