{"record":{"id":"ed1fd6988b5f64ce","repo":"invoke-ai/InvokeAI","slug":"unexpected-cond-image-shape-tuple-rgb-bchw-01-sh","errorCode":null,"errorMessage":"Unexpected cond image shape: {tuple(rgb_bchw_01.shape)} (expected B,3,H,W)","messagePattern":"Unexpected cond image shape: (.+?) \\(expected B,3,H,W\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/anima/control_net_lllite.py","lineNumber":80,"sourceCode":"def target_cond_hw(latent_h: int, latent_w: int, patch_spatial: int = 2) -> tuple[int, int]:\n    \"\"\"Return the (H, W) the cond image / mask must be resized to.\n\n    The LLLite ``conditioning1`` trunk has total conv stride 16, so the cond\n    image must be sized to ``latent_HW * 8`` in input pixel space (= token_HW\n    * 16 after DiT patchify with patch_spatial=2). The DiT internally pads the\n    latent up to a multiple of ``patch_spatial`` before patchify, so the same\n    rounding is mirrored here — otherwise odd latent dims yield a token-count\n    mismatch that silently bypasses every LLLite module.\n    \"\"\"\n    padded_h = ((latent_h + patch_spatial - 1) // patch_spatial) * patch_spatial\n    padded_w = ((latent_w + patch_spatial - 1) // patch_spatial) * patch_spatial\n    return padded_h * 8, padded_w * 8\n\n\ndef prepare_cond_image(rgb_bchw_01: torch.Tensor, latent_h: int, latent_w: int, patch_spatial: int = 2) -> torch.Tensor:\n    \"\"\"RGB image (B, 3, H, W) in [0, 1] -> (1, 3, H_t, W_t) in [-1, 1].\"\"\"\n    if rgb_bchw_01.ndim != 4 or rgb_bchw_01.shape[1] != 3:\n        raise ValueError(f\"Unexpected cond image shape: {tuple(rgb_bchw_01.shape)} (expected B,3,H,W)\")\n    img = rgb_bchw_01[:1]\n    target_h, target_w = target_cond_hw(latent_h, latent_w, patch_spatial)\n    if img.shape[-2] != target_h or img.shape[-1] != target_w:\n        img = F.interpolate(img, size=(target_h, target_w), mode=\"bicubic\", align_corners=False)\n        img = img.clamp(0.0, 1.0)\n    return img * 2.0 - 1.0\n\n\ndef prepare_mask(mask_b1hw_01: torch.Tensor, latent_h: int, latent_w: int, patch_spatial: int = 2) -> torch.Tensor:\n    \"\"\"Mask (B, 1, H, W) or (B, H, W) in [0, 1] -> (1, 1, H_t, W_t) in {0.0, 1.0}.\n\n    1 = inpaint area, 0 = keep. The caller is responsible for the ``*2-1``\n    rescale before concat with RGB (see :func:`build_inpaint_cond_image`).\n    \"\"\"\n    if mask_b1hw_01.ndim == 3:\n        m = mask_b1hw_01.unsqueeze(1)\n    elif mask_b1hw_01.ndim == 4 and mask_b1hw_01.shape[1] == 1:\n        m = mask_b1hw_01","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/anima/control_net_lllite.py#L62-L98","documentation":"prepare_cond_image converts an RGB conditioning image shaped (B,3,H,W) in [0,1] into the tensor ControlNet-LLLite expects, resizing to the latent-derived target. It raises ValueError when the tensor is not 4-D or its channel dimension is not 3 — i.e. it was passed grayscale, batched-with-alpha, or wrongly permuted data.","triggerScenarios":"Calling prepare_cond_image (directly or via _build_lllite_cond_image) with a tensor of shape (3,H,W) (missing batch dim), (B,1,H,W) grayscale, (B,H,W,3) NHWC layout, or ndim != 4.","commonSituations":"Loading images via PIL/cv2 and forgetting to permute HWC->CHW, passing a mask instead of an RGB image, passing a batch of 1 without the leading dimension, or using a 4-channel RGBA image.","solutions":["Ensure input is a float tensor with shape (B,3,H,W) and values in [0,1].","If shape is (H,W,3) or (3,H,W), add/permute: img.permute(2,0,1).unsqueeze(0).","Convert grayscale to RGB with .repeat(1,3,1,1) and drop alpha channels.","Validate with a guard before calling (see validationCode)."],"exampleFix":"# before\ncond = prepare_cond_image(image_np.transpose(2, 0, 1), latent_h, latent_w)\n# after\nimport torch\nimg = torch.from_numpy(image_np).permute(2, 0, 1).unsqueeze(0).float() / 255.0\nassert img.shape[1] == 3, img.shape\ncond = prepare_cond_image(img, latent_h, latent_w)","handlingStrategy":"validation","validationCode":"import torch\ndef validate_cond_image(img: torch.Tensor) -> None:\n    if not isinstance(img, torch.Tensor):\n        raise TypeError(\"cond image must be a torch.Tensor\")\n    if img.ndim != 4 or img.shape[1] != 3:\n        raise ValueError(f\"expected (B,3,H,W), got {tuple(img.shape)}\")\n    if img.min() < 0.0 or img.max() > 1.0:\n        raise ValueError(\"cond image values must be in [0,1]\")","typeGuard":"def is_rgb_bchw(t) -> bool:\n    import torch\n    return isinstance(t, torch.Tensor) and t.ndim == 4 and t.shape[1] == 3","tryCatchPattern":"try:\n    cond = prepare_cond_image(img, latent_h, latent_w)\nexcept ValueError as e:\n    raise ValueError(f\"bad conditioning image: {e}; provide float (B,3,H,W) in [0,1]\") from e","preventionTips":["Always convert HWC->CHW and add batch dim after PIL/cv2 load.","Convert RGBA to RGB (drop alpha).","Normalize to [0,1] (divide by 255 for uint8 inputs).","Unit-test the preprocessing pipeline with shape assertions."],"tags":["pytorch","shape-mismatch","controlnet","validation"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}