sgl-project/sglang · error · TypeError

Expected CHW image tensor with 1 or 3 channels, got shape {s

Error message

Expected CHW image tensor with 1 or 3 channels, got shape {shape}

What it means

After confirming the tensor is 3D CHW, the Step3-VL transform requires the channel dimension to be exactly 1 (grayscale, auto-replicated to 3) or 3 (RGB). Any other first dimension (e.g. 2, 4 for RGBA, or an HWC tensor misread as CHW) fails this check. This catches layout mistakes where width or an alpha channel ends up in the channel slot.

Source

Thrown at python/sglang/srt/multimodal/processors/step3_vl.py:43

Step3Image = Union[Image.Image, torch.Tensor]
ImageWithPatches = tuple[Step3Image, list[Step3Image], list[int] | None]


class GPUToTensor(torch.nn.Module):

    def forward(
        self, raw_image: Union[np.ndarray, Image.Image, torch.Tensor]
    ) -> torch.Tensor:
        if isinstance(raw_image, torch.Tensor):
            image_tensor = raw_image
            if image_tensor.ndim != 3:
                raise TypeError(
                    f"Expected CHW image tensor, got shape {tuple(image_tensor.shape)}"
                )
            if image_tensor.shape[0] == 1:
                image_tensor = image_tensor.repeat(3, 1, 1)
            elif image_tensor.shape[0] != 3:
                raise TypeError(
                    f"Expected CHW image tensor with 1 or 3 channels, got shape {tuple(image_tensor.shape)}"
                )
            if image_tensor.dtype == torch.uint8:
                image_tensor = image_tensor.to(torch.float32).div(255)
            elif not image_tensor.is_floating_point():
                image_tensor = image_tensor.to(torch.float32)
            return image_tensor.contiguous()
        if isinstance(raw_image, Image.Image):
            image_tensor = transforms.ToTensor()(raw_image)
            if torch.cuda.is_available():
                image_tensor = image_tensor.to(torch.device("cuda"))
            return image_tensor
        if raw_image.ndim == 2:
            raw_image = raw_image[:, :, None].repeat(3, -1)
        if torch.cuda.is_available():
            device = torch.device("cuda")
        else:
            device = torch.device("cpu")

View on GitHub (pinned to 0132848349)

Solutions

  1. Convert RGBA to RGB before tensorizing: image.convert('RGB')
  2. permute HWC to CHW so channels are dim 0
  3. For grayscale masks, keep 1 channel — it is auto-expanded to 3

Example fix

# before
img = torch.from_numpy(rgba_array)  # (H, W, 4)
img = img.permute(2, 0, 1)
# after
img = Image.fromarray(rgba_array).convert('RGB')
img = torch.from_numpy(np.array(img)).permute(2, 0, 1)
Defensive patterns

Strategy: validation

Validate before calling

if isinstance(t, torch.Tensor) and t.ndim == 3:
    assert t.shape[0] in (1, 3), f'bad channels: {tuple(t.shape)}'

Type guard

def has_valid_channels(t: torch.Tensor) -> bool:
    return t.ndim == 3 and t.shape[0] in (1, 3)

Prevention

When it happens

Trigger: Passing a CHW tensor with shape[0] not in {1,3}: a 4-channel RGBA tensor (4,H,W), or an HWC tensor of a 4-pixel-wide image permuted incorrectly so a non-channel dim lands first.

Common situations: PNG images with alpha loaded as (4,H,W); masks saved as 2-channel float arrays; tensors converted from HWC without permute so channels land in the wrong axis.

Related errors


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