invoke-ai/InvokeAI · error · ValueError

Invalid number of channels.

Error message

Invalid number of channels.

What it means

normalize_image_channel_count converts numpy image arrays to 3 channels: 1-channel is triplicated, 4-channel RGBA is alpha-blended onto white, 3-channel passes through. It raises this ValueError only if channels is not in {1,3,4} — e.g. a 2-channel or >4-channel array slipped past the caller. Note the code asserts channels in {1,3,4} just above, so hitting the raise usually means a build with -O0/asserts stripped or a shape confusion.

Source

Thrown at invokeai/backend/image_util/util.py:137

    """
    assert image.dtype == np.uint8
    if image.ndim == 2:
        image = image[:, :, None]
    assert image.ndim == 3
    _height, _width, channels = image.shape
    assert channels == 1 or channels == 3 or channels == 4
    if channels == 3:
        return image
    if channels == 1:
        return np.concatenate([image, image, image], axis=2)
    if channels == 4:
        color = image[:, :, 0:3].astype(np.float32)
        alpha = image[:, :, 3:4].astype(np.float32) / 255.0
        normalized = color * alpha + 255.0 * (1.0 - alpha)
        normalized = normalized.clip(0, 255).astype(np.uint8)
        return normalized

    raise ValueError("Invalid number of channels.")


def resize_image_to_resolution(input_image: np.ndarray, resolution: int) -> np.ndarray:
    """Resizes an image, fitting it to the given resolution.

    Adapted from https://github.com/huggingface/controlnet_aux (Apache-2.0 license).

    Args:
        input_image: The input image.
        resolution: The resolution to fit the image to.

    Returns:
        The resized image.
    """
    h = float(input_image.shape[0])
    w = float(input_image.shape[1])
    scaling_factor = float(resolution) / min(h, w)
    h = int(h * scaling_factor)

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Convert the input to RGB or grayscale before calling: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) or load with cv2.IMREAD_COLOR.
  2. Check image.shape — if the last dimension is not 1, 3, or 4, reshape or drop the extra channels before calling.
  3. If the array is (H,W,2), inspect the pipeline step that produced it; usually one channel is data and one is alpha — split or merge them appropriately.

Example fix

// before
edges = get_canny_edges(odd_array)  # odd_array.shape == (H, W, 2)
// after
assert odd_array.ndim == 3 and odd_array.shape[2] in (1, 3, 4)
rgb = cv2.cvtColor(odd_array, cv2.COLOR_BGR2RGB) if odd_array.shape[2] == 3 else odd_array[:, :, :3]
edges = get_canny_edges(rgb)
Defensive patterns

Strategy: type-guard

Validate before calling

def ensure_rgb_uint8(img: np.ndarray) -> np.ndarray:
    assert img.dtype == np.uint8
    if img.ndim == 2:
        img = img[:, :, None]
    if img.shape[2] not in (1, 3, 4):
        raise ValueError(f"expected 1/3/4 channels, got {img.shape[2]}")
    return img

Type guard

def is_normalizable_image(img: np.ndarray) -> bool:
    return img.dtype == np.uint8 and img.ndim == 3 and img.shape[2] in (1, 3, 4)

Try / catch

try:
    normalized = normalize_image_channel_count(img)
except ValueError:
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # or inspect img.shape and fix channels
    normalized = normalize_image_channel_count(img)

Prevention

When it happens

Trigger: Passing a numpy array with 2 channels (e.g. cv2 grayscale loaded with an odd flag or a (H,W,2) array), 5+ channels, or an accidental shape mismatch such as stacking two single-channel images. Called via np_img_resize, get_canny_edges, and controlnet run paths.

Common situations: Feeding video frames or exotic formats (e.g. YUV, 16-bit multi-channel) into controlnet preprocessing, concatenating arrays incorrectly, or an OpenCV load returning an unexpected channel layout (IMREAD_UNCHANGED on a 2-channel PNG).

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/1796c1ad5623d5c3. Report an issue: GitHub.