sgl-project/sglang · error · ValueError

Expected {len(image_token_counts)} image placeholder token(s

Error message

Expected {len(image_token_counts)} image placeholder token(s), found {placeholder_count}.

What it means

Raised by KimiK3ProcessorHelper._expand_k3_image_prompt_token_ids when the number of image placeholder tokens found in the already-tokenized input_ids does not equal the number of image token counts supplied. The library throws it because each <|IMAGE_TOKEN|> placeholder in the prompt must be expanded to per-image token sequences, so a mismatch would silently corrupt alignment.

Source

Thrown at python/sglang/srt/multimodal/processors/kimi_k3.py:98

    tokenizer,
) -> torch.Tensor:
    """Expand K3 image placeholders into the checkpoint's media contract.

    K3 requires each image feature span to be enclosed by its original uploaded
    dimensions.  The chat template deliberately emits one ``media_pad`` per
    image; after decode, insert the surrounding control tokens and expand that
    one placeholder to the NaViT feature count.
    """
    if len(image_token_counts) != len(image_sizes):
        raise ValueError("Expected one original size for each K3 image.")

    if isinstance(input_ids, torch.Tensor):
        input_ids = input_ids.detach().flatten().cpu().numpy()
    input_ids = np.asarray(input_ids, dtype=np.int64)

    placeholder_count = np.count_nonzero(input_ids == image_token_id)
    if placeholder_count != len(image_token_counts):
        raise ValueError(
            f"Expected {len(image_token_counts)} image placeholder token(s), "
            f"found {placeholder_count}."
        )

    output = []
    image_index = 0
    for token_id in input_ids:
        if token_id != image_token_id:
            output.append(int(token_id))
            continue

        width, height = image_sizes[image_index]
        output.extend(
            _encode_k3_special_tokens(
                tokenizer,
                f"<|media_begin|>image {width}x{height}<|media_content|>",
            )
        )

View on GitHub (pinned to 0132848349)

Solutions

  1. Verify the prompt string contains exactly one image token per supplied image before calling the API
  2. Check that input_ids were not truncated or sliced after tokenization (e.g. by context-length trimming)
  3. Regenerate artifacts so len(artifacts) matches the number of placeholders in the prompt

Example fix

# before
ids = tokenizer(prompt_with_2_placeholders).input_ids
expand(ids, image_token_counts=[c1, c2, c3])  # 3 counts, 2 placeholders
# after
assert ids.count(image_token_id) == len(image_token_counts)
expand(ids, image_token_counts=[c1, c2])
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
def check_ids(ids, image_token_id, counts):
    n = int(np.count_nonzero(np.asarray(ids) == image_token_id))
    assert n == len(counts), f"{n} placeholders vs {len(counts)} images"

Try / catch

try:
    expand(ids, counts)
except ValueError as e:
    if "placeholder" in str(e): re_tokenize_prompt_and_retry()
    else: raise

Prevention

When it happens

Trigger: Calling compose_request/get_mm_data with input_ids whose count of image_token_id occurrences differs from len(image_token_counts); typically after the tokenizer rendered fewer/more image placeholders than images provided, or input_ids were truncated/pre-trimmed.

Common situations: Prompt template omits or duplicates the image placeholder string per image; multimodal prefill chunking or truncation removes placeholder tokens; a cached/stale artifact list length differs from the re-tokenized prompt.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/c917faaf7a60268d. Report an issue: GitHub.