Comfy-Org/ComfyUI · error · ValueError
Expected 4D image tensor, got shape {tuple(images.shape)}
Error message
Expected 4D image tensor, got shape {tuple(images.shape)} What it means
_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.
Source
Thrown at comfy_extras/nodes_dataset.py:654
if has_process and has_group:
raise ValueError(
f"{cls.__name__}: Cannot override both _process and _group_process. "
"Override only one, or set is_group_process explicitly."
)
if not has_process and not has_group:
raise ValueError(
f"{cls.__name__}: Must override either _process or _group_process"
)
return has_group
@classmethod
def _ensure_image_list(cls, images):
"""Normalize to a flat list of [1, H, W, C] tensors."""
if isinstance(images, torch.Tensor):
if images.ndim != 4:
raise ValueError(f"Expected 4D image tensor, got shape {tuple(images.shape)}")
return [images[i:i+1] for i in range(images.shape[0])]
flat = []
for item in images:
if not isinstance(item, torch.Tensor) or item.ndim != 4:
raise ValueError(f"Expected 4D image tensor, got {type(item).__name__} shape {getattr(item, 'shape', None)}")
flat.extend([item[i:i+1] for i in range(item.shape[0])])
return flat
@classmethod
def define_schema(cls):
if cls.node_id is None:
raise NotImplementedError(f"{cls.__name__} must set node_id class variable")
is_group = cls._detect_processing_mode()
# Auto-detect is_output_list if not explicitly set
# Single processing: False (backend collects results into list)View on GitHub (pinned to 1c6d8d45b3)
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.
Example fix
# before images = images[0] # shape (H, W, C) # after images = images[0].unsqueeze(0) # shape (1, H, W, C)
Defensive patterns
Strategy: type-guard
Validate before calling
import torch
def as_batch(t):
if t.ndim == 3:
t = t.unsqueeze(0)
if t.ndim != 4:
raise ValueError(f"need 4D, got {tuple(t.shape)}")
return t Type guard
def is_4d_image_tensor(t) -> bool:
return isinstance(t, torch.Tensor) and t.ndim == 4 Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- folder_name must name a subfolder of the datasets directory,
- Expected 4D image tensor, got {type(item).__name__} shape {g
- INVALID_TAG_FILTER
- Connect at least one keyframe image.
- Spreading {len(images)} images across the clip needs an expl
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/caf27b2fe1f9d9e2.
Report an issue: GitHub.