sgl-project/sglang · error · ValueError

predict_num_frames supports a single prediction only, got sh

Error message

predict_num_frames supports a single prediction only, got shape {tuple(predicted_seconds.shape)}. One frame count cannot serve prompts with different natural durations.

What it means

predict_num_frames converts a predicted duration (seconds) into a single frame count, which only makes sense for exactly one sample. If the model was given a batch (or a multi-prompt tensor), one scalar frame count cannot represent different natural durations, so it errors when predicted_seconds.numel() != 1.

Source

Thrown at python/sglang/multimodal_gen/runtime/models/adapter/ltx_2_duration_head.py:136

    def predict_num_frames(
        self,
        video_tokens: torch.Tensor | None = None,
        audio_tokens: torch.Tensor | None = None,
        *,
        frame_rate: float,
        temporal_compression_ratio: int,
        min_seconds: float = 1.0,
        max_seconds: float = 20.0,
    ) -> int:
        """Predict a frame count on the VAE's causal temporal grid.

        Clamp first, then snap: a clamped count is not necessarily grid-aligned,
        so snapping first would give a different answer.
        """
        predicted_seconds = self(video_tokens, audio_tokens)
        if predicted_seconds.numel() != 1:
            raise ValueError(
                "predict_num_frames supports a single prediction only, got shape "
                f"{tuple(predicted_seconds.shape)}. One frame count cannot serve "
                "prompts with different natural durations."
            )
        seconds = predicted_seconds.item()

        # Floor at 1 so the grid arithmetic cannot go negative.
        min_frames = max(1, round(min_seconds * frame_rate))
        max_frames = round(max_seconds * frame_rate)
        clamped_frames = max(min_frames, min(round(seconds * frame_rate), max_frames))

        num_frames = (
            (clamped_frames - 1) // temporal_compression_ratio
        ) * temporal_compression_ratio + 1

        if num_frames < min_frames:
            # Flooring undershot the lower bound; take the next grid point up.
            snapped_up = num_frames + temporal_compression_ratio

View on GitHub (pinned to 0132848349)

Solutions

  1. Call predict_num_frames per sample: index/slice the token tensors to batch size 1 before calling
  2. Restructure the loop to iterate over batch items and collect per-item frame counts
  3. If you truly want one count for all prompts, pick per-sample prediction first and then aggregate deliberately

Example fix

# before
num_frames = head.predict_num_frames(video_tokens, audio_tokens)  # batched
# after
num_frames = [
    head.predict_num_frames(video_tokens[i:i+1], audio_tokens[i:i+1] if audio_tokens is not None else None)
    for i in range(video_tokens.shape[0])
]
Defensive patterns

Strategy: validation

Validate before calling

if video_tokens.shape[0] > 1:
    raise ValueError("call predict_num_frames per sample, batched input unsupported")
num_frames = head.predict_num_frames(video_tokens, audio_tokens)

Type guard

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

Try / catch

try:
    n = head.predict_num_frames(v, a)
except ValueError as e:
    if "single prediction only" in str(e):
        n = [head.predict_num_frames(v[i:i+1], None if a is None else a[i:i+1]) for i in range(v.shape[0])]
    else:
        raise

Prevention

When it happens

Trigger: Calling `head.predict_num_frames(video_tokens=..., audio_tokens=...)` with a batched tensor of shape (B, ...) where B > 1, since forward returns shape (B,) and numel() > 1.

Common situations: Scaling a single-sample generation script to batch inference; mixing per-prompt loops with vectorized batching; tests that accidentally pass a batch dimension of 2.

Related errors


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