matplotlib/matplotlib · error · ValueError

Third dimension must be 3 or 4

Error message

Third dimension must be 3 or 4

What it means

Colorizer.to_rgba converts already-colorized (M, N, 3) RGB or (M, N, 4) RGBA data to output RGBA. The last axis must be exactly 3 or 4 channels; any other third dimension is rejected. This is the raw-color path — scalar (M, N) data meant to go through the colormap does not belong here.

Source

Thrown at lib/matplotlib/colorizer.py:167

    @staticmethod
    def _pass_image_data(x, alpha=None, bytes=False, norm=True):
        """
        Helper function to pass ndarray of shape (...,3) or (..., 4)
        through `to_rgba()`, see `to_rgba()` for docstring.
        """
        if x.shape[2] == 3:
            if alpha is None:
                alpha = 1
            if x.dtype == np.uint8:
                alpha = np.uint8(alpha * 255)
            m, n = x.shape[:2]
            xx = np.empty(shape=(m, n, 4), dtype=x.dtype)
            xx[:, :, :3] = x
            xx[:, :, 3] = alpha
        elif x.shape[2] == 4:
            xx = x
        else:
            raise ValueError("Third dimension must be 3 or 4")
        if xx.dtype.kind == 'f':
            # If any of R, G, B, or A is nan, set to 0
            if np.any(nans := np.isnan(x)):
                if x.shape[2] == 4:
                    xx = xx.copy()
                xx[np.any(nans, axis=2), :] = 0

            if norm and (xx.max() > 1 or xx.min() < 0):
                raise ValueError("Floating point image RGB values "
                                 "must be in the [0,1] range")
            if bytes:
                xx = (xx * 255).astype(np.uint8)
        elif xx.dtype == np.uint8:
            if not bytes:
                xx = xx.astype(np.float32) / 255
        else:
            raise ValueError("Image RGB array must be uint8 or "
                             "floating point; found %s" % xx.dtype)

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Ensure the input is (M, N, 3) RGB or (M, N, 4) RGBA before calling to_rgba
  2. Squeeze stray trailing axes: np.squeeze on shape (M, N, 1) data
  3. For scalar (M, N) data, use the normal mappable/colormap path rather than the raw RGBA path

Example fix

// before
rgba = colorizer.to_rgba(pairs)   # pairs is (M, N, 2)
// after
rgba = colorizer.to_rgba(rgb)      # rgb is (M, N, 3) or (M, N, 4)
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np

if x.ndim != 3 or x.shape[-1] not in (3, 4):
    raise ValueError(f'expected (M, N, 3|4) color data, got {x.shape}')

Type guard

def is_rgba_like(a):
    return getattr(a, 'ndim', 0) == 3 and a.shape[-1] in (3, 4)

Prevention

When it happens

Trigger: colorizer.to_rgba(x) with x of shape (M, N, 2) (e.g. stacked x/y pairs), (M, N, 1) (an unsqueezed single channel), or (M, N, 5)+; passing pre-stacked arrays whose last axis is not color channels.

Common situations: Feeding coordinate or velocity-vector stacks into the color API by mistake; an extra squeeze/expand_dims bug leaving a trailing 1-axis; assuming any 3-D array is interpreted as multi-channel scalar data.

Related errors


AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21). Data as JSON: /api/errors/9e56b357a2da820a. Report an issue: GitHub.