sgl-project/sglang · error · ValueError

Block sparse tensors{context} require explicit sparse_block_

Error message

Block sparse tensors{context} require explicit sparse_block_size[0] to disambiguate block size for seqlen_q={seqlen_q} and num_m_blocks={num_m_blocks}.

What it means

Raised when sparse_block_size_q cannot be inferred: seqlen_q is divisible such that multiple q-block sizes could produce the observed num_m_blocks, and base_m_block != 1, so the layout is ambiguous. The user must pass sparse_block_size[0] explicitly.

Source

Thrown at python/sglang/kernels/ops/attention/flash_attn/cute/block_sparsity.py:348

    """
    base_m_block = q_stage * m_block_size
    base_n_block = n_block_size
    if sparse_block_size_kv is None:
        sparse_block_size_kv = base_n_block
    if sparse_block_size_kv != base_n_block:
        raise ValueError(
            f"Block sparse tensors{context} require BLOCK_SIZE_KV={base_n_block}."
        )
    if tensors.mask_block_idx is None:
        raise ValueError(
            "mask_block_cnt and mask_block_idx must be provided for block sparsity."
        )
    num_m_blocks = tensors.mask_block_idx.shape[2]

    if sparse_block_size_q is None:
        sparse_block_size_q = get_sparse_q_block_size(tensors, seqlen_q)
        if sparse_block_size_q is None and base_m_block != 1:
            raise ValueError(
                f"Block sparse tensors{context} require explicit sparse_block_size[0] "
                f"to disambiguate block size for seqlen_q={seqlen_q} and num_m_blocks={num_m_blocks}."
            )
        if sparse_block_size_q is None:
            sparse_block_size_q = ceildiv(seqlen_q, num_m_blocks)

    if sparse_block_size_q % base_m_block != 0:
        raise ValueError(
            f"Block sparse tensors{context} have block size {sparse_block_size_q}, "
            f"which must be a multiple of {base_m_block}."
        )

    expected_m_blocks = ceildiv(seqlen_q, sparse_block_size_q)
    expected_n_blocks = ceildiv(seqlen_k, sparse_block_size_kv)
    q_subtile_factor = sparse_block_size_q // base_m_block
    expected_count_shape = (batch_size, num_head, expected_m_blocks)
    expected_index_shape = (batch_size, num_head, expected_m_blocks, expected_n_blocks)

View on GitHub (pinned to 0132848349)

Solutions

  1. Pass sparse_block_size=(sparse_block_size_q, kv_size) explicitly to the op/normalize call
  2. Rebuild the BlockMask recording its q block size so it can be supplied later

Example fix

// before
out = fa(q, k, v, block_sparse_tensors=tensors)
// after
out = fa(q, k, v, block_sparse_tensors=tensors, sparse_block_size=(128, 128))
Defensive patterns

Strategy: validation

Validate before calling

if seqlen_q % num_m_blocks == 0 and base_m_block != 1 and sparse_block_size_q is None:
    sparse_block_size_q = seqlen_q // num_m_blocks  # pass explicitly

Prevention

When it happens

Trigger: Calling normalize_block_sparse_config without sparse_block_size_q, with seqlen_q that doesn't unambiguously map to num_m_blocks (e.g. seqlen_q=4096, num_m_blocks=32 could be q-block 128) and kernel q_stage*m_block_size != 1.

Common situations: Dynamically-shaped workloads where seqlen_q varies and the block size was only implied by the mask construction.

Related errors


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