{"record":{"id":"8165bd5947df0934","repo":"jax-ml/jax","slug":"expected-a-3-dim-mask-instead-got-mask-shape-8165bd","errorCode":null,"errorMessage":"Expected a 3-dim mask, instead got: {mask.shape=}","messagePattern":"Expected a 3-dim mask, instead got: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_mask_info.py","lineNumber":561,"sourceCode":"      a data type smaller than np.int32 (if possible).\n    head_shards: Number of head shards of the mesh in which the kernel is\n      launched.\n    q_seq_shards: Number of Q sequence shards of the mesh in which the kernel is\n      launched.\n    shrink_grid: Whether or not we should apply the grid shrinking optimization.\n\n  Returns:\n    `MaskInfo`, a sparse representation of the dense mask.\n    `MaskCallable`: a callable that, given in input Q and KV indices, returns\n      the value of the mask at those coordinates.\n\n  Raises:\n    ValueError: if the input mask is invalid or the block sizes are not\n    compatible with the mask sizes.\n  \"\"\"\n\n  if len(mask.shape) != 3:\n    raise ValueError(f'Expected a 3-dim mask, instead got: {mask.shape=}')\n\n  head_count, q_seq_len, kv_seq_len = mask.shape\n  q_block_size, kv_block_size = block_shape\n  q_blocks_count, q_mod = divmod(q_seq_len, q_block_size)\n  kv_blocks_count, kv_mod = divmod(kv_seq_len, kv_block_size)\n\n  if q_mod != 0:\n    raise ValueError(f'{q_block_size=} should divide {q_seq_len=}.')\n  if kv_mod != 0:\n    raise ValueError(f'{kv_block_size=} should divide {kv_seq_len=}.')\n\n  q_seq_len_per_shard, mod = divmod(q_seq_len, q_seq_shards)\n  if mod != 0:\n    raise ValueError(f'{q_seq_shards=} should divide {q_seq_len=}.')\n\n  q_blocks_per_shard, mod = divmod(q_seq_len_per_shard, q_block_size)\n  if mod != 0:\n    raise ValueError(f'{q_block_size=} should divide {q_seq_len_per_shard=}.')","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_mask_info.py#L543-L579","documentation":"_process_mask expects the attention mask to be a 3D array shaped (num_heads, q_seq_len, kv_seq_len). A 2D mask (batch, seq) or 4D mask (batch, heads, q, kv) fails this check immediately.","triggerScenarios":"Passing a standard Bert-style 2D padding mask or a 4D transformer mask directly to make_splash_attention_mask instead of the expected 3D (head, q, kv) layout.","commonSituations":"Porting a HuggingFace model whose attention_mask is (batch, seq); broadcasting a per-token mask without adding the head dimension first.","solutions":["Expand 2D masks to 3D: mask[None, :, :] repeated/tiled across heads","Squeeze the batch dimension from 4D masks (splash attention handles batching via scan/sharding, not a batch dim)","Tile per-head masks with jnp.tile(mask[None], (num_heads, 1, 1))"],"exampleFix":"# before\nmask_2d = tokens != 0  # (q, kv)\n# after\nmask_3d = jnp.tile(mask_2d[None], (num_heads, 1, 1))  # (heads, q, kv)","handlingStrategy":"type-guard","validationCode":"assert mask.ndim == 3, mask.shape","typeGuard":"def is_3d_mask(m) -> bool:\n    return getattr(m, 'ndim', -1) == 3","tryCatchPattern":null,"preventionTips":["Write a mask-shape helper that always returns (heads, q, kv)","Never feed HF attention_mask directly"],"tags":["jax","splash-attention","mask","rank-mismatch","tpu"],"backgroundTag":"rank-mismatch","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}