{"record":{"id":"caf27b2fe1f9d9e2","repo":"Comfy-Org/ComfyUI","slug":"expected-4d-image-tensor-got-shape-tuple-images","errorCode":null,"errorMessage":"Expected 4D image tensor, got shape {tuple(images.shape)}","messagePattern":"Expected 4D image tensor, got shape (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_extras/nodes_dataset.py","lineNumber":654,"sourceCode":"\n        if has_process and has_group:\n            raise ValueError(\n                f\"{cls.__name__}: Cannot override both _process and _group_process. \"\n                \"Override only one, or set is_group_process explicitly.\"\n            )\n        if not has_process and not has_group:\n            raise ValueError(\n                f\"{cls.__name__}: Must override either _process or _group_process\"\n            )\n\n        return has_group\n\n    @classmethod\n    def _ensure_image_list(cls, images):\n        \"\"\"Normalize to a flat list of [1, H, W, C] tensors.\"\"\"\n        if isinstance(images, torch.Tensor):\n            if images.ndim != 4:\n                raise ValueError(f\"Expected 4D image tensor, got shape {tuple(images.shape)}\")\n            return [images[i:i+1] for i in range(images.shape[0])]\n\n        flat = []\n        for item in images:\n            if not isinstance(item, torch.Tensor) or item.ndim != 4:\n                raise ValueError(f\"Expected 4D image tensor, got {type(item).__name__} shape {getattr(item, 'shape', None)}\")\n            flat.extend([item[i:i+1] for i in range(item.shape[0])])\n        return flat\n\n    @classmethod\n    def define_schema(cls):\n        if cls.node_id is None:\n            raise NotImplementedError(f\"{cls.__name__} must set node_id class variable\")\n\n        is_group = cls._detect_processing_mode()\n\n        # Auto-detect is_output_list if not explicitly set\n        # Single processing: False (backend collects results into list)","sourceCodeStart":636,"sourceCodeEnd":672,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_extras/nodes_dataset.py#L636-L672","documentation":"_ensure_image_list is the shared normalizer for the dataset processing node family: given a single torch tensor it must be 4D ([B, H, W, C]) so it can be sliced per-batch-item into [1, H, W, C] entries. A 3D tensor (single image without batch dim) or 5D tensor fails here with its full shape printed.","triggerScenarios":"Passing images.shape == (H, W, C) directly (missing batch dimension), or a 5D video-like tensor, into a node whose _process expects per-image [1,H,W,C] tensors.","commonSituations":"Slicing a batch with images[0] upstream (drops to 3D) and forgetting unsqueeze(0); mixing VAE-latent-shaped or video tensors into an image-processing dataset node.","solutions":["Add the batch dimension: images = images.unsqueeze(0) for a single image.","Remove extra dimensions for 5D input: select a frame or reshape to [B, H, W, C].","Pass the whole 4D batch straight through instead of pre-slicing it."],"exampleFix":"# before\nimages = images[0]           # shape (H, W, C)\n# after\nimages = images[0].unsqueeze(0)  # shape (1, H, W, C)","handlingStrategy":"type-guard","validationCode":"import torch\ndef as_batch(t):\n    if t.ndim == 3:\n        t = t.unsqueeze(0)\n    if t.ndim != 4:\n        raise ValueError(f\"need 4D, got {tuple(t.shape)}\")\n    return t","typeGuard":"def is_4d_image_tensor(t) -> bool:\n    return isinstance(t, torch.Tensor) and t.ndim == 4","tryCatchPattern":null,"preventionTips":["Always keep a batch dimension: use images[i:i+1], never images[i].","unsqueeze(0) single HWC images before passing them in.","Pass whole 4D batches straight to dataset nodes."],"tags":["dataset","tensor-shape","validation","comfyui"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}