sgl-project/sglang · error · ValueError
Kimi GPU preprocessing expects raw uint8 pixels, got {image.
Error message
Kimi GPU preprocessing expects raw uint8 pixels, got {image.dtype} What it means
Kimi K2.5 GPU preprocessing needs raw 0-255 uint8 pixels: the resize kernel rounds to integers and normalization folds in 1/255 scaling. A float tensor (e.g. already-normalized 0-1) would collapse to 0/1 after rounding, so the processor refuses it with ValueError instead of producing garbage.
Source
Thrown at python/sglang/srt/multimodal/processors/kimi_k25.py:134
def _ensure_chw_rgb(image: torch.Tensor) -> torch.Tensor:
"""Coerce an already-decoded (C, H, W) image tensor to 3-channel RGB.
PIL inputs are RGB-normalized by _pil_to_cuda_chw, but pre-decoded
tensor inputs (e.g. nvJPEG / cached CUDA tensors) keep their native
channel count. Grayscale (1ch) or RGBA (4ch) images then break the
downstream torch.cat over a batch of images, which requires a
consistent channel dimension. Normalize every tensor to 3 channels.
Also move the tensor to the GPU (matching _pil_to_cuda_chw) so a CPU
input does not trip a device mismatch against the CUDA normalization
constants downstream. No-op if already on the device.
"""
if image.dtype != torch.uint8:
# Raw 0-255 is load-bearing downstream: the resize rounds to integers
# and the normalization folds in a 1/255 scale, so a normalized float
# image would collapse to 0/1 and then be rescaled.
raise ValueError(
f"Kimi GPU preprocessing expects raw uint8 pixels, got {image.dtype}"
)
image = image.cuda()
if image.dim() == 2: # (H, W) grayscale -> (1, H, W)
image = image.unsqueeze(0)
c = image.shape[0]
if c == 3:
return image
if c == 1:
return image.repeat(3, 1, 1)
# RGBA or other multi-channel layouts: keep the first 3 channels.
return image[:3]
def _resize_bicubic_if_needed(
image: torch.Tensor, target_height: int, target_width: int
) -> torch.Tensor:
"""Track the checkpoint processor's ``PIL.Image.resize(..., BICUBIC)``.View on GitHub (pinned to 0132848349)
Solutions
- Pass raw uint8 images (PIL->np.asarray, cv2 BGR->RGB uint8) into the processor
- If you only have normalized floats, denormalize and rescale back to uint8 before sending
- Keep one canonical raw-image path and do normalization only inside the processor
Example fix
# before
img = torchvision.transforms.ToTensor()(pil) # float 0-1 CHW
to_cuda_chw(img)
# after
img = torch.from_numpy(np.asarray(pil.convert("RGB"))) # uint8 HWC
to_cuda_chw(img) Defensive patterns
Strategy: type-guard
Validate before calling
assert image.dtype == torch.uint8, f"need raw uint8 pixels, got {image.dtype}" Type guard
def is_raw_uint8(img: torch.Tensor) -> bool:
return img.dtype == torch.uint8 Try / catch
try:
chw = _to_cuda_chw(image)
except ValueError as e:
if "raw uint8" in str(e):
image = (image.clamp(0, 1) * 255).to(torch.uint8) # denormalize
chw = _to_cuda_chw(image)
else:
raise Prevention
- Feed raw PIL/numpy uint8 images; let the processor normalize
- Never reuse outputs of other preprocessors as inputs
- Keep one canonical raw-image representation in your pipeline
When it happens
Trigger: Calling _to_cuda_chw / _ensure_chw_rgb with a float image tensor (already normalized or 0-1 floats) instead of a uint8 HxWx3/CHW tensor.
Common situations: Reusing images already preprocessed by another pipeline (HF processor output, torchvision ToTensor, normalized caches) and feeding them directly into Kimi's GPU path.
Related errors
- Expected {len(image_token_counts)} image placeholder token(s
- Kimi image placeholders must map one-to-one to image data: e
- Kimi image placeholders must map one-to-one to image data: e
- Grid dim ({_mm_grid_attrs[modality]}) not found in {mm_input
- Invalid Kimi image grid metadata: {values}; expected [h, w]
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/a23f1bf113a84ee7.
Report an issue: GitHub.