invoke-ai/InvokeAI · error · ValueError

Unexpected mask shape: {tuple(mask_b1hw_01.shape)} (expected

Error message

Unexpected mask shape: {tuple(mask_b1hw_01.shape)} (expected B,H,W or B,1,H,W)

What it means

prepare_mask accepts a mask as (B,H,W) or (B,1,H,W) floats in [0,1]; anything else raises ValueError. It binarizes at 0.5 and resizes (nearest) to the latent-derived spatial target, so it must be able to interpret the tensor's layout unambiguously.

Source

Thrown at invokeai/backend/anima/control_net_lllite.py:100

    target_h, target_w = target_cond_hw(latent_h, latent_w, patch_spatial)
    if img.shape[-2] != target_h or img.shape[-1] != target_w:
        img = F.interpolate(img, size=(target_h, target_w), mode="bicubic", align_corners=False)
        img = img.clamp(0.0, 1.0)
    return img * 2.0 - 1.0


def prepare_mask(mask_b1hw_01: torch.Tensor, latent_h: int, latent_w: int, patch_spatial: int = 2) -> torch.Tensor:
    """Mask (B, 1, H, W) or (B, H, W) in [0, 1] -> (1, 1, H_t, W_t) in {0.0, 1.0}.

    1 = inpaint area, 0 = keep. The caller is responsible for the ``*2-1``
    rescale before concat with RGB (see :func:`build_inpaint_cond_image`).
    """
    if mask_b1hw_01.ndim == 3:
        m = mask_b1hw_01.unsqueeze(1)
    elif mask_b1hw_01.ndim == 4 and mask_b1hw_01.shape[1] == 1:
        m = mask_b1hw_01
    else:
        raise ValueError(f"Unexpected mask shape: {tuple(mask_b1hw_01.shape)} (expected B,H,W or B,1,H,W)")
    m = m[:1].float()
    target_h, target_w = target_cond_hw(latent_h, latent_w, patch_spatial)
    if m.shape[-2] != target_h or m.shape[-1] != target_w:
        m = F.interpolate(m, size=(target_h, target_w), mode="nearest")
    return (m >= 0.5).float()


def build_inpaint_cond_image(rgb_pm1: torch.Tensor, mask01: torch.Tensor, masked_input: bool) -> torch.Tensor:
    """rgb_pm1: (1, 3, H, W) in [-1, 1], mask01: (1, 1, H, W) in {0, 1}. Returns (1, 4, H, W).

    The mask channel is rescaled to [-1, +1] (matching the RGB range), and if
    ``masked_input`` is set the RGB is zeroed where ``mask >= 0.5``.
    """
    if masked_input:
        keep = (mask01 < 0.5).to(rgb_pm1.dtype)
        rgb_pm1 = rgb_pm1 * keep
    mask_pm1 = mask01.to(rgb_pm1.dtype) * 2.0 - 1.0
    return torch.cat([rgb_pm1, mask_pm1], dim=1)

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Pass shape (B,H,W) or (B,1,H,W); squeeze extra channels: mask[:, :1] or mask[:, 0].
  2. Add the batch dim for a single mask: mask.unsqueeze(0).
  3. Convert RGB masks to single-channel first (e.g. take luminance or alpha).
  4. Binarize/normalize values to [0,1] so the >=0.5 threshold behaves as intended.

Example fix

# before
m = prepare_mask(mask_rgb, latent_h, latent_w)  # (B,3,H,W) -> ValueError
# after
mask_gray = mask_rgb.mean(dim=1, keepdim=True)  # (B,1,H,W)
m = prepare_mask(mask_gray, latent_h, latent_w)
Defensive patterns

Strategy: validation

Validate before calling

import torch
def validate_mask(mask: torch.Tensor) -> None:
    if not isinstance(mask, torch.Tensor):
        raise TypeError("mask must be a torch.Tensor")
    ok = mask.ndim == 3 or (mask.ndim == 4 and mask.shape[1] == 1)
    if not ok:
        raise ValueError(f"expected (B,H,W) or (B,1,H,W), got {tuple(mask.shape)}")
    if mask.ndim == 3:
        mask = mask.unsqueeze(1)
    if mask.min() < 0.0 or mask.max() > 1.0:
        raise ValueError("mask values must be in [0,1]")

Type guard

def is_mask_b1hw(t) -> bool:
    import torch
    return isinstance(t, torch.Tensor) and (t.ndim == 3 or (t.ndim == 4 and t.shape[1] == 1))

Try / catch

try:
    m = prepare_mask(mask, latent_h, latent_w)
except ValueError as e:
    raise ValueError(f"bad mask: {e}; provide (B,H,W) or (B,1,H,W) in [0,1]") from e

Prevention

When it happens

Trigger: Calling prepare_mask (or _build_lllite_cond_image) with an RGB (B,3,H,W) tensor, a (1,H,W)-only-mask confusion where ndim==4 but channel != 1, a (H,W) 2-D tensor, or a (B,4,H,W) RGBA mask.

Common situations: Passing the cond image where a mask is expected, forgetting unsqueeze(0) on a single (H,W) mask, or loading a multi-channel mask from an RGBA PNG without selecting one channel.

Understand the failure class

Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.

Related errors


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