sgl-project/sglang · error · ValueError

video rows {int(rows.shape[0])} must be divisible by t*h*w {

Error message

video rows {int(rows.shape[0])} must be divisible by t*h*w {rows_per_sample} for latent_shape={list(latent_shape)}

What it means

The unpatchify path requires the total number of token rows to be a whole multiple of t*h*w (rows per video sample implied by latent_shape). A remainder means rows cannot be evenly reshaped into per-frame patch grids, indicating a batch/row-count inconsistency between the token tensor and the declared latent shape.

Source

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

    *,
    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)
    t, h, w, channel = _int_tuple(latent_shape, "latent_shape", 4)
    pt, ph, pw = _int_tuple(patch_size, "patch_size", 3)
    expected_dim = pt * ph * pw * channel
    if int(rows.shape[-1]) != expected_dim:
        raise ValueError(
            f"video token dim {int(rows.shape[-1])} != patch volume * channel "
            f"{expected_dim} for latent_shape={list(latent_shape)}, "
            f"patch_size={[pt, ph, pw]}"
        )
    rows_per_sample = t * h * w
    if int(rows.shape[0]) % rows_per_sample:
        raise ValueError(
            f"video rows {int(rows.shape[0])} must be divisible by t*h*w "
            f"{rows_per_sample} for latent_shape={list(latent_shape)}"
        )
    packed = rows.reshape(-1, t, h, w, channel, pt, ph, pw)
    latent = torch.einsum("nthwcrpq->nctrhpwq", packed)
    return latent.reshape(-1, channel, t * pt, h * ph, w * pw).contiguous()


def minimax_h3_unpack_audio_tokens(
    rows: torch.Tensor,
    *,
    audio_t: int,
    audio_channel: int,
) -> torch.Tensor:
    """Unpack DiT audio token rows into SGLang audio VAE latent [C,latent_dim,T]."""

    _rank(rows, "audio token rows", 2)
    audio_t = int(audio_t)

View on GitHub (pinned to 0132848349)

Solutions

  1. Split the rows buffer per video (using the layout's block slices) and unpatchify each with its own latent_shape.
  2. Ensure latent_shape's t,h,w exactly match the frames/resolution the rows were generated from.
  3. Recompute latent_shape from the plan rather than caching it across requests.

Example fix

# before
latent = minimax_h3_unpatchify_video_tokens(all_rows, latent_shape=one_clip_shape, patch_size=patch)

# after
for sl, shape in zip(block_slices, block_latent_shapes):
    latent_i = minimax_h3_unpatchify_video_tokens(all_rows[sl], latent_shape=shape, patch_size=patch)
Defensive patterns

Strategy: validation

Validate before calling

t, h, w, _ = latent_shape
rows_per_sample = t * h * w
assert rows.shape[0] % rows_per_sample == 0, f"row count {rows.shape[0]} not a multiple of {rows_per_sample}"

Type guard

def rows_partition_cleanly(rows, latent_shape) -> bool:
    t, h, w, _ = latent_shape
    return rows.shape[0] % (t * h * w) == 0

Prevention

When it happens

Trigger: Passing rows for 5.5 samples' worth of grids relative to latent_shape, e.g. rows.shape[0]=1000 with t*h*w=256 (1000%256!=0); commonly concatenating reference and target video rows but declaring latent_shape for only one video.

Common situations: Concatenating multiple videos' rows (hybrid ref+target layouts) while unpatchifying with a single clip's latent_shape; dropped/duplicated rows during scheduling or attention masking; a stale latent_shape after changing resolution or frame count.

Related errors


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