huggingface/transformers · error · ValueError

Invalid image shape. Expected either {expected_ndims + 1} or

Error message

Invalid image shape. Expected either {expected_ndims + 1} or {expected_ndims} dimensions, but got {images.ndim} dimensions.

What it means

Raised by `transformers.image_utils.make_list_of_images` (default `expected_ndims=3`) when the input is a valid image type but its rank does not equal `expected_ndims` (single image) or `expected_ndims + 1` (batch). The helper's job is to normalize input into a Python list of images, so it must decide 'single vs batch'; an ambiguous rank makes that decision impossible. PIL images bypass this because they carry no batch dimension.

Source

Thrown at src/transformers/image_utils.py:192

            dimensions, an error is raised.
    """
    if is_batched(images):
        return images

    # Either the input is a single image, in which case we create a list of length 1
    if is_pil_image(images):
        # PIL images are never batched
        return [images]

    if is_valid_image(images):
        if images.ndim == expected_ndims + 1:
            # Batch of images
            images = list(images)
        elif images.ndim == expected_ndims:
            # Single image
            images = [images]
        else:
            raise ValueError(
                f"Invalid image shape. Expected either {expected_ndims + 1} or {expected_ndims} dimensions, but got"
                f" {images.ndim} dimensions."
            )
        return images
    raise ValueError(
        f"Invalid image type. Expected either PIL.Image.Image, numpy.ndarray, or torch.Tensor, but got {type(images)}."
    )


def make_flat_list_of_images(
    images: list[ImageInput] | ImageInput,
    expected_ndims: int = 3,
) -> ImageInput:
    """
    Ensure that the output is a flat list of images. If the input is a single image, it is converted to a list of length 1.
    If the input is a nested list of images, it is converted to a flat list of images.
    Args:
        images (`Union[list[ImageInput], ImageInput]`):

View on GitHub (pinned to a597f97485)

Solutions

  1. Restore the channel axis: `img = img[..., None]` or `np.expand_dims(img, -1)` for a 2D grayscale array.
  2. Match the rank to what the processor expects — keep 3 dims (C, H, W) for single images, 4 dims (N, C, H, W) for batches.
  3. If the input is genuinely a batch, ensure it stayed 4D after loading/squeezing.
  4. Convert to PIL first: PIL images are never ndim-checked.

Example fix

// before
gray = np.array(pil_gray_img)          # shape (H, W)
imgs = make_list_of_images(gray)       # ValueError: got 2 dimensions

// after
gray = np.array(pil_gray_img)[..., None]  # (H, W, 1)
imgs = make_list_of_images(gray)          # ok, treated as single image
Defensive patterns

Strategy: validation

Validate before calling

def as_3d_image(img):
    if hasattr(img, "ndim") and img.ndim == 2:  # grayscale lost its channel axis
        img = img[..., None]
    assert img.ndim in (3, 4), f"expected 3 (single) or 4 (batch) dims, got {img.ndim}"
    return img

Type guard

def is_acceptable_image_rank(img, expected_ndims: int = 3) -> bool:
    return img.ndim in (expected_ndims, expected_ndims + 1) if hasattr(img, "ndim") else True  # PIL passes

Prevention

When it happens

Trigger: Passing a 2D grayscale array (H, W) or 2D mask when expected_ndims=3; a 3D array where a 4D (batched) input was expected (expected_ndims=4 in some video/multi-frame processors); or a 5D tensor to a standard image path. Triggered via `processor.preprocess()` for processors that call this helper, or by calling it directly.

Common situations: Feeding grayscale/medical/multispectral data that lost its channel axis; squeezing a batch dimension too aggressively (`img.squeeze()` on (1, H, W, 1) gives (H, W)); or reusing a 3D shape where a video processor expects clips of shape (frames, C, H, W).

Related errors


AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14). Data as JSON: /api/errors/77181e22e01e85b8. Report an issue: GitHub.