{"record":{"id":"351bd31939c95766","repo":"jax-ml/jax","slug":"nesting-multiheadmasks-is-not-supported","errorCode":null,"errorMessage":"Nesting MultiHeadMasks is not supported","messagePattern":"Nesting MultiHeadMasks is not supported","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_mask.py","lineNumber":196,"sourceCode":"\n  masks: Sequence[Mask]\n\n  def __post_init__(self):\n    if not self.masks:\n      raise ValueError('Unsupported empty tuple of masks')\n\n    shape = self.masks[0].shape\n    for mask in self.masks[1:]:\n      if shape != mask.shape:\n        raise ValueError(\n            f'Unexpected mask shape, got: {mask.shape}, expected: {shape}'\n        )\n\n    if not all(isinstance(mask, Mask) for mask in self.masks):\n      raise ValueError('masks should be of type Mask')\n\n    if any(isinstance(mask, MultiHeadMask) for mask in self.masks):\n      raise ValueError('Nesting MultiHeadMasks is not supported')\n\n  @property\n  def shape(self) -> tuple[int, ...]:\n    return (len(self.masks),) + self.masks[0].shape\n\n  def __getitem__(self, idx) -> np.ndarray:\n    if len(idx) != 3:\n      raise NotImplementedError(f'Unsupported slice: {idx}')\n\n    head_slice = idx[0]\n    if isinstance(head_slice, int):\n      assert head_slice >= 0 and head_slice <= len(self.masks)\n      return self.masks[head_slice][idx[1:]]\n    else:\n      slice_masks = [mask[idx[1:]] for mask in self.masks[head_slice]]\n      return np.stack(slice_masks)\n\n  def __eq__(self, other: object):","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/experimental/pallas/ops/tpu/splash_attention/splash_attention_mask.py#L178-L214","documentation":"A MultiHeadMask whose elements include another MultiHeadMask is rejected: nesting would create ragged per-head structures that the kernel's per-head lowering cannot handle. Each element must be a flat (q, kv) Mask.","triggerScenarios":"MultiHeadMask([...]) where any element was produced by combining multiple masks via MultiHeadMask instead of '|'; passing a MultiHeadMask inside the masks list during programmatic construction.","commonSituations":"Trying to express grouped-query or grouped-head masks by nesting; recursive mask-building utilities that wrap their result in MultiHeadMask at every level; merging per-group masks into a multi-head wrapper.","solutions":["Flatten nested MultiHeadMasks into a single list of per-head masks before wrapping","Combine same-shape alternatives with '|' or '&' rather than nesting wrappers","If all heads share one mask, use a single NumpyMask or rank-3 array instead"],"exampleFix":"// before\nleft = MultiHeadMask([m]*4); right = MultiHeadMask([n]*4)\ncombined = MultiHeadMask([left, right])\n// after\ncombined = MultiHeadMask([mi | ni for mi, ni in zip([m]*4, [n]*4)])","handlingStrategy":"type-guard","validationCode":"assert not any(isinstance(m, MultiHeadMask) for m in masks)","typeGuard":"def is_flat_mask_list(ms):\n    return all(isinstance(m, Mask) and not isinstance(m, MultiHeadMask) for m in ms)","tryCatchPattern":null,"preventionTips":["Flatten nested MultiHeadMasks with itertools.chain","Combine same-shape options with | instead of nesting"],"tags":["jax","splash-attention","mask","multi-head","nesting"],"backgroundTag":"nested-structure-unsupported","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}