sgl-project/sglang · error · ValueError

combined_history=True requires direction=0 (bidi)

Error message

combined_history=True requires direction=0 (bidi)

What it means

In the chunkwise bidirectional gated-deltanet Triton path, phase_b_triton's combined_history option (writing forward and backward scan history into one buffer) is only implemented for direction=0, the combined bidirectional launch. Passing combined_history=True together with direction=1 or -1 (single-direction passes) is contradictory and rejected.

Source

Thrown at python/sglang/kernels/ops/diffusion/attention/sana_wm_gdn_chunkwise_triton.py:880

    kernel never touches — callers must discard the slot they didn't ask for.
    Reverse scan always seeds from zero (upstream bidi convention: only forward
    state is cached).
    """
    BH = I_P_kv.shape[0]
    _, _, BLOCK_D, _ = A.shape  # A is always full [BH, F, BLOCK_D, BLOCK_D]
    device, fdtype = I_P_kv.device, torch.float32

    if num_warps is None or num_stages is None or use_acc_fusion is None:
        _, _, b_w, b_s, b_acc, *_ = _get_arch_config(dot_precision, device=device)
        if num_warps is None:
            num_warps = b_w
        if num_stages is None:
            num_stages = b_s
        if use_acc_fusion is None:
            use_acc_fusion = b_acc

    if combined_history and direction != 0:
        raise ValueError("combined_history=True requires direction=0 (bidi)")

    # Kernel is DIRECTION-gated (constexpr), so inactive buffers can be 1-element
    # placeholders — frees ~4× M_fwd-shaped allocs per single-direction call.
    decay_flat = decay.reshape(BH, F).contiguous().float()

    load_init = init_state_kv is not None
    dummy = torch.empty(1, device=device, dtype=fdtype)

    def full_M():
        return torch.empty(BH, F, BLOCK_D, BLOCK_D, device=device, dtype=fdtype)

    def full_z():
        return torch.empty(BH, F, BLOCK_D, device=device, dtype=fdtype)

    M_fwd = dummy if direction == 2 else full_M()
    z_fwd = dummy if (direction == 2 or skip_z) else full_z()
    # Combined-history reuses M_fwd/z_fwd as M_hist/z_hist; rev outputs are
    # placeholders even though DIRECTION!=1.

View on GitHub (pinned to 0132848349)

Solutions

  1. Use combined_history=True only with the default direction=0
  2. For single-direction passes, pass combined_history=False (or None) and use the per-direction history buffers

Example fix

# before
phase_b_triton(..., direction=1, combined_history=True)
# after
phase_b_triton(..., direction=1, combined_history=False)
Defensive patterns

Strategy: validation

Validate before calling

assert not (combined_history and direction != 0), 'combined_history requires direction=0'

Prevention

When it happens

Trigger: Calling phase_b_triton(..., combined_history=True, direction=1) — i.e. requesting the combined-history output while running only one scan direction.

Common situations: Refactoring fused_bigdn_bidi_chunkwise into separate per-direction calls while keeping the combined_history flag, or copying parameters from the bidi call site into a single-direction invocation.

Related errors


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