sgl-project/sglang · error · ValueError
update_mask length mismatch: {update_mask.shape[0]} != {vide
Error message
update_mask length mismatch: {update_mask.shape[0]} != {video_logits.shape[0]} What it means
Before zeroing out condition rows in output logits, forward validates that update_mask (flattened) length equals the gathered video_logits row count. A mismatch means the mask was built for a different token set than the logits.
Source
Thrown at python/sglang/multimodal_gen/runtime/models/dits/minimax_h3.py:2638
torch.cat((video_logits, audio_logits), dim=-1), dim=0
)
video_logits, audio_logits = logits.split(
(video_width, logits.shape[-1] - video_width), dim=-1
)
# Preserve the full-row output GEMM (and therefore its numerical
# contract), but defer TP column gathers until after dead text/padding
# rows have been removed. For hybrid TP+Ulysses, the preceding SP row
# gather also carries only the TP-local output width.
video_logits = video_logits.index_select(0, infer_out_pos.to(device))
audio_logits = audio_logits.index_select(0, audio_pos.to(device))
if get_tp_world_size() > 1:
video_logits = tensor_model_parallel_all_gather(video_logits)
audio_logits = tensor_model_parallel_all_gather(audio_logits)
if not skip_mask_out_condition:
update_mask = update_mask.view(-1).to(device)
if update_mask.shape[0] != video_logits.shape[0]:
raise ValueError(
"update_mask length mismatch: "
f"{update_mask.shape[0]} != {video_logits.shape[0]}"
)
video_logits = video_logits * update_mask.unsqueeze(-1)
# Audio has no condition rows in the supported tasks, so its
# derived update mask is all ones. Honor an explicit mask when
# provided.
update_audio_mask = kwargs.get("update_audio_mask")
if update_audio_mask is not None:
audio_logits = audio_logits * update_audio_mask.view(-1).unsqueeze(-1)
return video_logits, audio_logits
EntryClass = MiniMaxH3DiTModel
__all__ = [
"MINIMAX_H3_FP32_BUFFER_NAMES",
"MINIMAX_H3_FP32_PARAM_NAMES",View on GitHub (pinned to 0132848349)
Solutions
- Build update_mask over exactly the video token rows of the packed sequence (length must equal video_logits.shape[0] after all-gather)
- If you pass token_tags, prefer deriving the mask from tags so lengths stay consistent
- Verify skip_mask_out_condition when condition zeroing is not needed
Example fix
// before model(..., update_mask=local_mask) # length != gathered video rows // after video_mask = torch.ones(num_video_rows, device=x.device) video_mask[cond_video_rows] = 0 model(..., update_mask=video_mask)
Defensive patterns
Strategy: validation
Validate before calling
update_mask = update_mask.view(-1) assert update_mask.shape[0] == num_gathered_video_rows, (update_mask.shape[0], num_gathered_video_rows)
Prevention
- Derive update_mask from the same token tags used for packing
- Remember TP all-gather restores full row counts — build masks globally
When it happens
Trigger: Passing an update_mask sized to the pre-TP-gather local rows, or to text+video tokens, while video_logits has the full gathered video-row count.
Common situations: TP all-gather changing row counts vs. locally-built masks; update masks built before packing/padding changes; masks computed for audio rows instead of video rows.
Related errors
- MiniMax H3 attention heads must be divisible by TP size: {ar
- TP size must be positive.
- MiniMax H3 {name}={value} must be divisible by TP size {tp_s
- TP-local heads {local_heads} not divisible by Ulysses world
- Failed to load MiniMax H3 Qwen3-VL weight {name!r}: checkpoi
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/f7da28beec300f37.
Report an issue: GitHub.