{"record":{"id":"c62d0665a688818b","repo":"jax-ml/jax","slug":"start-must-satisfy-a-ndim-start-a-ndim","errorCode":null,"errorMessage":"{start=} must satisfy {-a_ndim}<=start<={a_ndim}","messagePattern":"(.+?) must satisfy (.+?)<=start<=(.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/numpy/lax_numpy.py","lineNumber":8626,"sourceCode":"\n    Roll axis 1 to the end of the array:\n\n    >>> jnp.rollaxis(a, 1, a.ndim).shape\n    (2, 4, 5, 3)\n\n    Equivalent of these two with :func:`~jax.numpy.moveaxis`\n\n    >>> jnp.moveaxis(a, 2, 0).shape\n    (4, 2, 3, 5)\n    >>> jnp.moveaxis(a, 1, -1).shape\n    (2, 4, 5, 3)\n  \"\"\"\n  a = util.ensure_arraylike(\"rollaxis\", a)\n  start = core.concrete_or_error(operator.index, start, \"'start' argument of jnp.rollaxis()\")\n  a_ndim = np.ndim(a)\n  axis = _canonicalize_axis(axis, a_ndim)\n  if not (-a_ndim <= start <= a_ndim):\n    raise ValueError(f\"{start=} must satisfy {-a_ndim}<=start<={a_ndim}\")\n  if start < 0:\n    start += a_ndim\n  if start > axis:\n    start -= 1\n  return moveaxis(a, axis, start)\n\n\n@export\n@api.jit(static_argnames=('axis', 'bitorder'))\ndef packbits(a: ArrayLike, axis: int | None = None, bitorder: str = \"big\") -> Array:\n  \"\"\"Pack array of bits into a uint8 array.\n\n  JAX implementation of :func:`numpy.packbits`\n\n  Args:\n    a: N-dimensional array of bits to pack.\n    axis: optional axis along which to pack bits. If not specified, ``a`` will\n      be flattened.","sourceCodeStart":8608,"sourceCodeEnd":8644,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/numpy/lax_numpy.py#L8608-L8644","documentation":"Raised by jnp.rollaxis when the start parameter is outside [-ndim, ndim] of the input array; start must be a valid position to move the axis to, after canonicalization of axis itself.","triggerScenarios":"jnp.rollaxis(a, axis=0, start=5) on a 3-D array; start derived from user config or loop arithmetic exceeding the rank; start passed as a traced value is also rejected earlier by concrete_or_error.","commonSituations":"Hardcoded start values breaking after arrays are reshaped to lower rank; looping axis reordering with unclamped counters; mixing up rollaxis(start) vs moveaxis destination conventions.","solutions":["Clamp or validate start within [-a.ndim, a.ndim] before calling","Compute start adaptively from a.ndim rather than hardcoding","Prefer jnp.moveaxis(a, source, destination) which has clearer semantics and its own validation"],"exampleFix":"// before\njnp.rollaxis(a, axis=1, start=4)  # a.ndim == 3\n// after\njnp.moveaxis(a, source=1, destination=2)  # explicit, validated destination\n","handlingStrategy":"validation","validationCode":"start = int(start)\nn = jnp.ndim(a)\nif not (-n <= start <= n): raise ValueError(f'start {start} out of range for ndim {n}')","typeGuard":"def valid_rollaxis_start(a, start):\n    n = jnp.ndim(a)\n    return -n <= int(start) <= n","tryCatchPattern":null,"preventionTips":["Derive start from a.ndim, not constants","Prefer jnp.moveaxis for clarity","Validate axis params from config before use"],"tags":["jax","rollaxis","argument-validation"],"backgroundTag":"axis-out-of-range","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}