sgl-project/sglang · error · ValueError

Validate failed: S({S}) must be divisible by F({F}).

Error message

Validate failed: S({S}) must be divisible by F({F}).

What it means

When scale/shift is 4D (B, F, 1, D) — per-frame modulation — the sequence length S must be divisible by the number of frames F, because each frame's modulation is broadcast over S/F tokens. validate_scale_shift enforces S % F == 0.

Source

Thrown at python/sglang/kernels/ops/diffusion/norm/scale_residual_norm_cutedsl.py:229

def validate_scale_shift(t: torch.Tensor, B: int, S: int, D: int):
    if t.dtype not in (torch.float16, torch.bfloat16, torch.float32):
        raise ValueError(f"Validate failed: unsupported dtype: {t.dtype}")
    failed = False
    if t.ndim == 1 and (t.shape[0] not in (1, D)):
        failed = True
    elif t.ndim == 2 and ((t.shape[0] not in (1, B)) or t.shape[1] != D):
        failed = True
    elif t.ndim == 3 and (
        (t.shape[0] not in (1, B)) or (t.shape[1] not in (1, S) or t.shape[2] != D)
    ):
        failed = True
    elif t.ndim == 4:
        F = t.shape[1]
        if t.shape[0] != B or t.shape[2] != 1 or t.shape[3] != D:
            failed = True
        elif S % F != 0:
            raise ValueError(f"Validate failed: S({S}) must be divisible by F({F}).")
    if failed:
        raise ValueError(f"Validate failed: unsupported tensor shape: {t.shape}.")
    if t.stride()[-1] != 1:
        raise ValueError("Validate failed: not contiguous on dim D.")


def validate_gate(t: Union[torch.Tensor, int], B: int, S: int, D: int):
    if not isinstance(t, torch.Tensor):
        return
    validate_scale_shift(t, B, S, D)


@torch.library.custom_op("sglang::fused_norm_scale_shift", mutates_args=())
def fused_norm_scale_shift(
    x: torch.Tensor,
    weight: Optional[torch.Tensor],
    bias: Optional[torch.Tensor],
    scale: torch.Tensor,

View on GitHub (pinned to 0132848349)

Solutions

  1. Make S a multiple of F: fix the token count/patchify config so frames map evenly onto tokens
  2. Verify F equals the frame count actually used to build the latent, not a stale value
  3. Reshape modulation to a supported 1D (D or 1) or 2D (1/B, D) form if per-frame modulation isn't needed

Example fix

# before: S=256 tokens but F=7 frames
fused_norm_scale_shift(x, w, b, scale_4d, shift_4d, "rms")
# after: fix patchify so tokens_per_frame * F == S, e.g. S=256, F=8
assert S % F == 0
Defensive patterns

Strategy: validation

Validate before calling

if scale.ndim == 4:
    F = scale.shape[1]
    assert S % F == 0, f"S={S} not divisible by F={F}"

Prevention

When it happens

Trigger: Passing a 4D scale/shift with t.shape == (B, F, 1, D) where F does not divide the activation sequence length S (e.g. S=100, F=7).

Common situations: Video diffusion where img token count doesn't evenly match the number of latent frames after a patchify/tokenization change (e.g. patch size or token merge changed S without updating F).

Related errors


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