{"record":{"id":"298e5ace54fa7101","repo":"xai-org/x-algorithm","slug":"block-sparse-tensors-context-require-explicit-spa","errorCode":null,"errorMessage":"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}.","messagePattern":"Block sparse tensors(.+?) require explicit sparse_block_size\\[0\\] to disambiguate block size for seqlen_q=(.+?) and num_m_blocks=(.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"phoenix/xrex/cutedsl/ranker_fa4/block_sparsity.py","lineNumber":319,"sourceCode":"    q_stage: int,\n    context: str,\n    sparse_block_size_q: int | None = None,\n    sparse_block_size_kv: int | None = None,\n) -> Tuple[Tuple[int, int, int], Tuple[int, int, int, int], int]:\n    base_m_block = q_stage * m_block_size\n    base_n_block = n_block_size\n    if sparse_block_size_kv is None:\n        sparse_block_size_kv = base_n_block\n    if sparse_block_size_kv != base_n_block:\n        raise ValueError(f\"Block sparse tensors{context} require BLOCK_SIZE_KV={base_n_block}.\")\n    if tensors.mask_block_idx is None:\n        raise ValueError(\"mask_block_cnt and mask_block_idx must be provided for block sparsity.\")\n    num_m_blocks = tensors.mask_block_idx.shape[2]\n\n    if sparse_block_size_q is None:\n        sparse_block_size_q = get_sparse_q_block_size(tensors, seqlen_q)\n        if sparse_block_size_q is None and base_m_block != 1:\n            raise ValueError(\n                f\"Block sparse tensors{context} require explicit sparse_block_size[0] \"\n                f\"to disambiguate block size for seqlen_q={seqlen_q} and num_m_blocks={num_m_blocks}.\"\n            )\n        if sparse_block_size_q is None:\n            sparse_block_size_q = ceildiv(seqlen_q, num_m_blocks)\n\n    if sparse_block_size_q % base_m_block != 0:\n        raise ValueError(\n            f\"Block sparse tensors{context} have block size {sparse_block_size_q}, \"\n            f\"which must be a multiple of {base_m_block}.\"\n        )\n\n    expected_m_blocks = ceildiv(seqlen_q, sparse_block_size_q)\n    expected_n_blocks = ceildiv(seqlen_k, sparse_block_size_kv)\n    q_subtile_factor = sparse_block_size_q // base_m_block\n    expected_count_shape = (batch_size, num_head, expected_m_blocks)\n    expected_index_shape = (batch_size, num_head, expected_m_blocks, expected_n_blocks)\n","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/phoenix/xrex/cutedsl/ranker_fa4/block_sparsity.py#L301-L337","documentation":"When sparse_block_size_q is not given, the library tries to infer it from get_sparse_q_block_size(tensors, seqlen_q); if inference returns None and base_m_block != 1, the Q block size is ambiguous (multiple block sizes could produce the observed num_m_blocks), so an explicit sparse_block_size[0] is required.","triggerScenarios":"Calling normalize_block_sparse_config without sparse_block_size_q while the mask tensors lack enough information to infer the Q block size and q_stage * m_block_size > 1 — e.g. seqlen_q that divides evenly under several candidate block sizes.","commonSituations":"Using a custom seqlen_q or packing strategy where ceildiv(seqlen_q, num_m_blocks) is not the true block size; switching from per-token (base_m_block==1) to multi-token M-blocks without updating config.","solutions":["Pass sparse_block_size_q explicitly (sparse_block_size[0]) matching how the mask metadata was generated","Regenerate mask tensors with a well-defined Q block size so inference succeeds","Verify num_m_blocks = ceildiv(seqlen_q, sparse_block_size_q) for your chosen size"],"exampleFix":"# before\ncfg = normalize_block_sparse_config(tensors, ..., sparse_block_size=None)\n\n# after\nq_bs = ceil(seqlen_q / tensors.mask_block_idx.shape[2])\ncfg = normalize_block_sparse_config(tensors, ..., sparse_block_size=(q_bs, None))","handlingStrategy":"validation","validationCode":"from math import ceil\nnum_m = tensors.mask_block_idx.shape[2]\nq_bs = ceil(seqlen_q / num_m)\nassert q_bs * num_m >= seqlen_q  # then pass (q_bs, None) explicitly","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass sparse_block_size_q explicitly when base_m_block > 1","Store the block size used to generate masks alongside the metadata"],"tags":["block-sparsity","config-validation","ambiguous-inference"],"backgroundTag":"ambiguous-block-size-config","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}