sgl-project/sglang · error · ValueError

Kimi image placeholders must map one-to-one to image data: e

Error message

Kimi image placeholders must map one-to-one to image data: expected {expected_image_count}, loaded {len(base_output.images)}

What it means

Post-load consistency check in Kimi K2.5's process_mm_data_async: after fast_load_mm_data / the text-scanning loader returns, base_output.images must still have exactly expected_image_count entries. The comment notes only the text-scanning loader can deviate; if it does, the request is rejected.

Source

Thrown at python/sglang/srt/multimodal/processors/kimi_k25.py:583

            base_output = await self.fast_load_mm_data(
                prompt=input_text,
                image_data=image_data,
                multimodal_tokens=self.mm_tokens,
                # fast_load_mm_data, unlike load_mm_data, does not derive
                # input_ids from the prompt; without this the wrapper falls back
                # to re-tokenizing the expanded string.
                input_ids=input_text,
            )
        else:
            base_output = await self.load_mm_data(
                prompt=input_text,
                image_data=image_data,
                multimodal_tokens=self.mm_tokens,
            )
            # Only the text-scanning loader can come back with a different
            # count; fast_load_mm_data fills one slot per image_data entry.
            if len(base_output.images) != expected_image_count:
                raise ValueError(
                    "Kimi image placeholders must map one-to-one to image data: "
                    f"expected {expected_image_count}, loaded {len(base_output.images)}"
                )

        mm_items, input_ids, _ = await self.process_and_combine_mm_data_async(
            base_output,
            self.mm_tokens,
            sglang_original_input_ids=base_output.input_ids,
        )

        # K2.5/K2.7 encoder-DP assigns an image to exactly one TP rank. Keep
        # its GPU transport proxy lazy until that assignment is known, avoiding a full
        # image copy to every rank. The scheduler only honors this marker once
        # the processor has already set the item's hash and pad value.
        if self.keep_mm_features_on_device and self.server_args.mm_enable_dp_encoder:
            for item in mm_items:
                item.model_specific_data[DEFER_CUDA_IPC_FEATURE_RECONSTRUCTION_KEY] = (
                    True

View on GitHub (pinned to 0132848349)

Solutions

  1. If you override the loader, return exactly one image per image_data entry; filter the request before loading instead
  2. Drop corrupt images from both image_data and the prompt placeholders together so counts stay aligned
  3. Check loader logs for silently swallowed load failures

Example fix

# before (custom loader drops unreadable images)
class MyLoader:
    async def fast_load_mm_data(self, prompt, image_data, multimodal_tokens):
        return BaseOutput(images=[i for i in loaded if i.ok()])  # count can shrink

# after
class MyLoader:
    async def fast_load_mm_data(self, prompt, image_data, multimodal_tokens):
        assert len(images) == len(image_data)
        return BaseOutput(images=images)  # fail loudly, never shrink
Defensive patterns

Strategy: validation

Validate before calling

out = await proc.fast_load_mm_data(prompt=input_text, image_data=image_data, multimodal_tokens=proc.mm_tokens)
assert len(out.images) == len(image_data)

Try / catch

try:
    mm_items, input_ids, _ = await proc.process_and_combine_mm_data_async(base_output, ...)
except ValueError as e:
    if "loaded" in str(e):
        raise HttpClientError("image load failed; some images could not be fetched") from e
    raise

Prevention

When it happens

Trigger: A custom or text-scanning mm data loader returns a different number of images than were passed in image_data while placeholders matched, triggering the len(base_output.images) != expected_image_count check.

Common situations: Custom loader subclasses overriding fast_load_mm_data and filtering/dropping images (e.g. skipping unreadable files), or duplicate/failed URLs collapsing during loading.

Related errors


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