sgl-project/sglang · error · ValueError

head_dim must be a multiple of 8, got {head_dim}.

Error message

head_dim must be a multiple of 8, got {head_dim}.

What it means

The 3D RoPE embeddings in the LTX-2.5 diffusion decoder split head_dim into temporal and spatial rotation pairs; a head_dim not divisible by 8 cannot be split into whole even-sized halves, so init fails fast with this check.

Source

Thrown at python/sglang/multimodal_gen/runtime/models/decoders/ltx_2_5_diffusion_decoder.py:236

        _compile=True,
    )
    if len(_BLOCK_MASK_CACHE) >= _BLOCK_MASK_CACHE_MAX:
        _BLOCK_MASK_CACHE.pop(next(iter(_BLOCK_MASK_CACHE)))
    _BLOCK_MASK_CACHE[cache_key] = block_mask
    return block_mask


class LTX2VideoVaeRotaryPosEmbed3D(nn.Module):
    """Absolute 3D rotary embedding over the (T, H, W) grid.

    `head_dim` splits into (T, H, W) chunks, each rotated by its own axis
    position.
    """

    def __init__(self, head_dim: int, base: float = 10000.0) -> None:
        super().__init__()
        if head_dim % 8 != 0:
            raise ValueError(f"head_dim must be a multiple of 8, got {head_dim}.")
        # A quarter to T, the rest split H/W, both kept even for whole
        # rotation pairs.
        dim_t = (head_dim // 4) // 2 * 2
        dim_hw = (head_dim - dim_t) // 2
        if dim_hw % 2 != 0:
            dim_t -= 2
            dim_hw = (head_dim - dim_t) // 2
        self.rope_dim_split = (dim_t, dim_hw, dim_hw)
        self.base = base

    def _axis_tables(
        self, length: int, dim: int, device: torch.device
    ) -> tuple[torch.Tensor, torch.Tensor]:
        exponents = torch.arange(0, dim, 2, dtype=torch.float64, device=device) / dim
        inv_freqs = (1.0 / self.base**exponents).to(torch.float32)
        positions = torch.arange(length, dtype=torch.float32, device=device)
        angles = positions[:, None] * inv_freqs[None, :]
        return angles.cos(), angles.sin()

View on GitHub (pinned to 0132848349)

Solutions

  1. Use a head_dim that is a multiple of 8 (64, 128, 32, ...)
  2. Keep the model's shipped default head_dim (usually 64) unless you have re-derived compatible dims
  3. Validate arch head_dim at config-load time before constructing the decoder

Example fix

# before
rope = LtxRotaryEmbedding3D(head_dim=50)
# after
rope = LtxRotaryEmbedding3D(head_dim=48)  # or 64
Defensive patterns

Strategy: validation

Validate before calling

if head_dim % 8 != 0:
    raise ValueError(f"head_dim {head_dim} must be a multiple of 8")

Type guard

def valid_head_dim(head_dim: int) -> bool:
    return head_dim > 0 and head_dim % 8 == 0

Try / catch

try:
    rope = LtxRotaryEmbedding3D(head_dim=head_dim)
except ValueError:
    head_dim = (head_dim // 8) * 8 or 8
    rope = LtxRotaryEmbedding3D(head_dim=head_dim)

Prevention

When it happens

Trigger: Constructing the RoPE module with head_dim values like 12, 20, 48+4, or any non-multiple of 8 (e.g. head_dim=50), typically via a custom arch config overriding decoder head_dim.

Common situations: Porting a model whose attention head_dim is unusual; hand-editing architecture hyperparameters; experimenting with smaller head dims to shrink parameters.

Related errors


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