sgl-project/sglang · error · ValueError

img_position_ids must be [1, S, 3], got {list(img_position_i

Error message

img_position_ids must be [1, S, 3], got {list(img_position_ids.shape)}

What it means

MiniMax H3's RoPE embedding module expects image position ids of exact shape [1, S, 3] (batch 1, sequence S, coords t/h/w). It validates dims and shape[0]==1 and raises otherwise, since downstream indexing img_position_ids[0] assumes that layout.

Source

Thrown at python/sglang/multimodal_gen/runtime/models/dits/minimax_h3.py:425

class MiniMaxH3Rope(nn.Module):
    """3D rope over (t, h, w); rotates 96 of 128 head dims (rotary_percent 0.75).

    Frequency layout concatenates temporal, height, and width embeddings twice,
    with 16 frequencies per axis (inv_freq = base^-(arange(0,32,2)/32)).
    """

    def __init__(self, inv_freq_len: int) -> None:
        super().__init__()
        self.register_buffer(
            "inv_freq",
            torch.empty(inv_freq_len, dtype=_FP32_DTYPE),
            persistent=True,
        )

    def forward(self, img_position_ids: torch.Tensor) -> torch.Tensor:
        """img_position_ids: [1, S, 3] (t, h, w) -> freqs [S, rot_dim=96]."""
        if img_position_ids.dim() != 3 or img_position_ids.shape[0] != 1:
            raise ValueError(
                "img_position_ids must be [1, S, 3], got "
                f"{list(img_position_ids.shape)}"
            )
        pos = img_position_ids[0].to(_FP32_DTYPE)  # [S, 3]
        per_axis = pos.unsqueeze(-1) * self.inv_freq.view(1, 1, -1)  # [S, 3, 16]
        t_f, h_f, w_f = per_axis.unbind(dim=1)  # each [S, 16]
        half = torch.cat((t_f, h_f, w_f), dim=-1)  # [S, 48]
        return torch.cat((half, half), dim=-1)  # [S, 96]


def _rope_cos_sin_cache(freqs: torch.Tensor, *, dtype: torch.dtype) -> torch.Tensor:
    """Build the activation-dtype cos|sin cache for fused Q/K RoPE."""
    half = freqs.shape[-1] // 2
    return (
        torch.cat(
            (torch.cos(freqs[:, :half]), torch.sin(freqs[:, :half])),
            dim=-1,
        )

View on GitHub (pinned to 0132848349)

Solutions

  1. Reshape to exactly [1, S, 3] before calling forward: ids.squeeze(0) if batched per-sample then process one at a time, or ids[0:1] to take batch dim of 1
  2. If truly batching, loop over the batch or ensure the module is called per sample since it only supports batch 1
  3. Fix the position-id generator to emit [1, S, 3] (t, h, w stacked on the last axis)

Example fix

# before
freqs = rope(img_position_ids)  # shape [B, S, 3]

# after
assert img_position_ids.shape[0] == 1
freqs = rope(img_position_ids)  # pass ids[0:1] or per-sample slice
Defensive patterns

Strategy: type-guard

Validate before calling

assert img_position_ids.dim() == 3 and img_position_ids.shape[0] == 1 and img_position_ids.shape[-1] == 3, 'need [1, S, 3]'

Type guard

def is_valid_pos_ids(t: torch.Tensor) -> bool:
    return t.dim() == 3 and t.shape[0] == 1 and t.shape[2] == 3

Prevention

When it happens

Trigger: Passing img_position_ids with batch > 1 (e.g. [B,S,3] from a batched pipeline), a squeezed [S,3] tensor, or a 4-D tensor; common when callers broadcast or stack per-sample position ids.

Common situations: Batched generation code producing [B,S,3] position ids; position-id builders from other models (LLM-style [1,S] or [B,S]) reused here; accidental unsqueeze/expand of the coords.

Related errors


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