{"record":{"id":"37fdfb8f349dab61","repo":"sgl-project/sglang","slug":"video-token-dim-int-rows-shape-1-patch-vol","errorCode":null,"errorMessage":"video token dim {int(rows.shape[-1])} != patch volume * channel {expected_dim} for latent_shape={list(latent_shape)}, patch_size={[pt, ph, pw]}","messagePattern":"video token dim (.+?) != patch volume \\* channel (.+?) for latent_shape=(.+?), patch_size=(.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/minimax_h3/packed_tokens.py","lineNumber":57,"sourceCode":"    packed = latent.reshape(batch, channel, t, pt, h, ph, w, pw)\n    packed = torch.einsum(\"nctrhpwq->nthwcrpq\", packed)\n    return packed.reshape(batch * t * h * w, channel * pt * ph * pw).contiguous()\n\n\ndef minimax_h3_unpatchify_video_tokens(\n    rows: torch.Tensor,\n    *,\n    latent_shape: Sequence[int],\n    patch_size: Sequence[int],\n) -> torch.Tensor:\n    \"\"\"Unpack DiT video token rows into SGLang latent [B,C,T,H,W].\"\"\"\n\n    _rank(rows, \"video token rows\", 2)\n    t, h, w, channel = _int_tuple(latent_shape, \"latent_shape\", 4)\n    pt, ph, pw = _int_tuple(patch_size, \"patch_size\", 3)\n    expected_dim = pt * ph * pw * channel\n    if int(rows.shape[-1]) != expected_dim:\n        raise ValueError(\n            f\"video token dim {int(rows.shape[-1])} != patch volume * channel \"\n            f\"{expected_dim} for latent_shape={list(latent_shape)}, \"\n            f\"patch_size={[pt, ph, pw]}\"\n        )\n    rows_per_sample = t * h * w\n    if int(rows.shape[0]) % rows_per_sample:\n        raise ValueError(\n            f\"video rows {int(rows.shape[0])} must be divisible by t*h*w \"\n            f\"{rows_per_sample} for latent_shape={list(latent_shape)}\"\n        )\n    packed = rows.reshape(-1, t, h, w, channel, pt, ph, pw)\n    latent = torch.einsum(\"nthwcrpq->nctrhpwq\", packed)\n    return latent.reshape(-1, channel, t * pt, h * ph, w * pw).contiguous()\n\n\ndef minimax_h3_unpack_audio_tokens(\n    rows: torch.Tensor,\n    *,","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/minimax_h3/packed_tokens.py#L39-L75","documentation":"minimax_h3_unpatchify_video_tokens reconstructs a latent from token rows and checks that the per-token feature dim equals pt*ph*pw*channel as implied by latent_shape and patch_size. A mismatch means the rows tensor was produced with a different patch size, channel count, or latent shape than declared, so the inverse einsum would silently corrupt data if allowed.","triggerScenarios":"Calling unpatchify with latent_shape=(2,8,8,16) and patch_size=(1,16,16) on rows whose last dim is 1*8*8*16 (produced with patch_size (1,8,8)); or rows carrying an extra projected feature dimension from a DiT output head.","commonSituations":"Changing patch_size or channel config between patchify and unpatchify calls; feeding raw DiT hidden states instead of the projected token rows; a channel mismatch after switching VAE versions.","solutions":["Pass the same patch_size and channel count that were used when the rows were patchified.","If rows carry extra features, project/select down to pt*ph*pw*channel dims before unpatchify.","Persist patch_size/latent_shape alongside the token rows (e.g. in the plan/state dict) so round-trips use matching params."],"exampleFix":"# before\nlatent = minimax_h3_unpatchify_video_tokens(rows, latent_shape=shape, patch_size=(1, 8, 8))\n\n# after\nlatent = minimax_h3_unpatchify_video_tokens(rows, latent_shape=shape, patch_size=(1, 16, 16))  # match patchify","handlingStrategy":"validation","validationCode":"pt, ph, pw = patch_size\nexpected = pt * ph * pw * latent_shape[3]\nassert rows.shape[-1] == expected, f\"token dim {rows.shape[-1]} != expected {expected}\"","typeGuard":"def rows_match_layout(rows, latent_shape, patch_size) -> bool:\n    pt, ph, pw = patch_size\n    return rows.ndim == 2 and rows.shape[-1] == pt * ph * pw * latent_shape[-1]","tryCatchPattern":null,"preventionTips":["Carry patch_size and latent_shape through the pipeline state alongside token rows.","Never change patch/channel config between patchify and unpatchify without regenerating rows."],"tags":["validation","tensor-shape","unpatchify","minimax-h3"],"backgroundTag":"tensor-dimension-mismatch","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}