{"record":{"id":"591f0e5364c2a674","repo":"jax-ml/jax","slug":"index-is-out-of-bounds-for-axis-axis-with-size-0","errorCode":null,"errorMessage":"index is out of bounds for axis {axis} with size 0","messagePattern":"index is out of bounds for axis (.+?) with size 0","errorType":"validation","errorClass":"IndexError","httpStatus":null,"severity":"error","filePath":"jax/_src/numpy/indexing.py","lineNumber":431,"sourceCode":"    squeeze_axes: list[int] = []\n    newaxis_dims: list[int] = []\n\n    expanded = self.expand_ellipses()\n    for pidx in expanded.indices:\n      if pidx.typ in [IndexType.ARRAY, IndexType.BOOLEAN, IndexType.ELLIPSIS]:\n        raise RuntimeError(f\"Internal: unexpected index encountered: {pidx}\")\n      elif pidx.typ == IndexType.NONE:\n        # Expanded axes indices are based on the rank of the array after slicing\n        # (tracked by start_indices) and squeezing (tracked by squeeze_axes), and\n        # expand_dims inserts dimensions in order, so we must also account for\n        # previous expanded dimensions.\n        newaxis_dims.append(len(start_indices) - len(squeeze_axes) + len(newaxis_dims) )\n      elif pidx.typ == IndexType.INTEGER:\n        assert isinstance(pidx.index, (int, np.integer))\n        axis, = pidx.consumed_axes\n        if core.definitely_equal(self.shape[axis], 0):\n          # XLA gives error when indexing into an axis of size 0\n          raise IndexError(f\"index is out of bounds for axis {axis} with size 0\")\n        start_index = int(pidx.index)\n        if normalize_indices and start_index < 0:\n          start_index += self.shape[axis]\n        # Normalization & validation have already been handled, so clip start_index\n        # to valid range\n        start_index = min(max(start_index, 0), self.shape[axis] - 1)\n        start_indices.append(start_index)\n        limit_indices.append(start_index + 1)\n        strides.append(1)\n        squeeze_axes.append(axis)\n      elif pidx.typ == IndexType.SLICE:\n        assert isinstance(pidx.index, slice)\n        axis, = pidx.consumed_axes\n        size = self.shape[axis]\n        start, stop, stride = pidx.index.indices(size)\n        if stride < 0:\n          new_start = min(size, stop + 1 + abs(start - stop - 1) % abs(stride))\n          start_indices.append(new_start)","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/numpy/indexing.py#L413-L449","documentation":"to_static_slice explicitly rejects integer indexing into an axis whose size is statically 0, because the underlying XLA slice op would error or produce invalid results on empty axes. Even though the index is a scalar, selecting from an empty axis is out of bounds.","triggerScenarios":"x = jnp.zeros((0, 4)); x[0] — any integer index on a statically-empty axis via the static-slice path.","commonSituations":"Pipeline stages where a batch/filter step legitimately produces zero rows and downstream code unconditionally indexes row 0; empty splits from data filtering.","solutions":["Guard on shape before indexing: if x.shape[0]: ...","Use x[:1] (slice) which safely yields an empty result on empty axes","Fix upstream filtering so the empty case is handled explicitly"],"exampleFix":"// before\nfirst = x[0]\n// after\nfirst = x[:1]  # empty-safe","handlingStrategy":"validation","validationCode":"if x.shape[axis] == 0:\n    raise ValueError(f'axis {axis} is empty')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use x[:1] instead of x[0] when axes may be empty","Guard empty-batch branches explicitly"],"tags":["jax","empty-array","out-of-bounds","indexing"],"backgroundTag":"index-into-empty-array","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}