sgl-project/sglang · error · ValueError

video latent spatial/time dims must be divisible by patch_si

Error message

video latent spatial/time dims must be divisible by patch_size: shape={list(latent.shape)}, patch_size={[pt, ph, pw]}

What it means

minimax_h3_patchify_video_latent reshapes a [B,C,T,H,W] latent into patch grid tokens, which requires T divisible by pt, H by ph, and W by pw. If any spatial/temporal dim is not a multiple of the patch size, reshape is impossible and this ValueError reports both shape and patch_size.

Source

Thrown at python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/minimax_h3/packed_tokens.py:34


def _rank(tensor: torch.Tensor, name: str, rank: int) -> None:
    if tensor.ndim != rank:
        raise ValueError(f"{name} must be rank {rank}, got shape={list(tensor.shape)}")


def minimax_h3_patchify_video_latent(
    latent: torch.Tensor,
    *,
    patch_size: Sequence[int],
) -> torch.Tensor:
    """Pack SGLang video latent [B,C,T,H,W] into DiT token rows."""

    _rank(latent, "video latent", 5)
    pt, ph, pw = _int_tuple(patch_size, "patch_size", 3)
    batch, channel, full_t, full_h, full_w = (int(dim) for dim in latent.shape)
    if full_t % pt or full_h % ph or full_w % pw:
        raise ValueError(
            "video latent spatial/time dims must be divisible by patch_size: "
            f"shape={list(latent.shape)}, patch_size={[pt, ph, pw]}"
        )
    t, h, w = full_t // pt, full_h // ph, full_w // pw
    packed = latent.reshape(batch, channel, t, pt, h, ph, w, pw)
    packed = torch.einsum("nctrhpwq->nthwcrpq", packed)
    return packed.reshape(batch * t * h * w, channel * pt * ph * pw).contiguous()


def minimax_h3_unpatchify_video_tokens(
    rows: torch.Tensor,
    *,
    latent_shape: Sequence[int],
    patch_size: Sequence[int],
) -> torch.Tensor:
    """Unpack DiT video token rows into SGLang latent [B,C,T,H,W]."""

    _rank(rows, "video token rows", 2)

View on GitHub (pinned to 0132848349)

Solutions

  1. Pre-crop or pad the latent so T%pt==0, H%ph==0, W%pw==0 before patchify.
  2. Ensure upstream encoding (VAE stride / frame count) is chosen so latent dims are patch-multiples (e.g. frame counts multiple of pt*vale_temporal_stride).
  3. Verify the patch_size tuple matches the model's configured patchification.

Example fix

# before
rows = minimax_h3_patchify_video_latent(latent, patch_size=(2, 16, 16))  # T=7

# after
t = (latent.shape[2] // 2) * 2
rows = minimax_h3_patchify_video_latent(latent[:, :, :t], patch_size=(2, 16, 16))
Defensive patterns

Strategy: validation

Validate before calling

pt, ph, pw = patch_size
b, c, t, h, w = latent.shape
assert t % pt == 0 and h % ph == 0 and w % pw == 0, "latent dims not patch-divisible"

Type guard

def patch_divisible(latent: torch.Tensor, patch_size) -> bool:
    pt, ph, pw = patch_size
    _, _, t, h, w = latent.shape
    return t % pt == 0 and h % ph == 0 and w % pw == 0

Prevention

When it happens

Trigger: Calling with latent shape (1,16,7,64,64) and patch_size (2,16,16) — the temporal dim 7 is not divisible by 2; or a VAE that produced a spatial dim of 1000 with pw=128.

Common situations: Mixed-resolution inputs where images/videos are not pre-cropped to the patch grid; a VAE downsampling factor that yields odd temporal dims for short clips (e.g. 5 frames with temporal patch 2); changing patch_size in config without regenerating latents.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/fe32936e3f586b9d. Report an issue: GitHub.