{"record":{"id":"bbb5ed3d05fecff8","repo":"jax-ml/jax","slug":"breakpoints-must-be-non-negative-and-less-than-len","errorCode":null,"errorMessage":"Breakpoints must be non-negative and less than length of data along given axis.","messagePattern":"Breakpoints must be non-negative and less than length of data along given axis\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/scipy/signal.py","lineNumber":539,"sourceCode":"    >>> with jnp.printoptions(precision=3):  # suppress float error\n    ...   print(\"Detrended:\", detrended)\n    ...   print(\"Underlying trend:\", data - detrended)\n    Detrended: [-5. -2.  2.  2.  3.]\n    Underlying trend: [6. 6. 6. 6. 6.]\n  \"\"\"\n  if overwrite_data is not None:\n    raise NotImplementedError(\"overwrite_data argument not implemented.\")\n  if type not in ['constant', 'linear']:\n    raise ValueError(\"Trend type must be 'linear' or 'constant'.\")\n  data_arr, = promote_dtypes_inexact(jnp.asarray(data))\n  if type == 'constant':\n    return data_arr - data_arr.mean(axis, keepdims=True)\n  else:\n    N = data_arr.shape[axis]\n    # bp is static, so we use np operations to avoid pushing to device.\n    bp_arr = np.sort(np.unique(np.r_[0, bp, N]))\n    if bp_arr[0] < 0 or bp_arr[-1] > N:\n      raise ValueError(\"Breakpoints must be non-negative and less than length of data along given axis.\")\n    data_arr = jnp.moveaxis(data_arr, axis, 0)\n    shape = data_arr.shape\n    data_arr = data_arr.reshape(N, -1)\n    for m in range(len(bp_arr) - 1):\n      Npts = bp_arr[m + 1] - bp_arr[m]\n      A = jnp.vstack([\n        jnp.ones(Npts, dtype=data_arr.dtype),\n        jnp.arange(1, Npts + 1, dtype=data_arr.dtype) / Npts.astype(data_arr.dtype)\n      ]).T\n      sl = slice(bp_arr[m], bp_arr[m + 1])\n      coef, *_ = linalg.lstsq(A, data_arr[sl])\n      data_arr = data_arr.at[sl].add(-jnp.matmul(A, coef, precision=lax.Precision.HIGHEST))\n    return jnp.moveaxis(data_arr.reshape(shape), 0, axis)\n\n\ndef _fft_helper(x: Array, win: Array, detrend_func: Callable[[Array], Array],\n                nperseg: int, noverlap: int, nfft: int | None, sides: str) -> Array:\n  \"\"\"Calculate windowed FFT in the same way the original SciPy does.","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/scipy/signal.py#L521-L557","documentation":"For linear detrending, breakpoints are combined with 0 and N (data length along axis) and must lie within [0, N]. A negative breakpoint or one exceeding the axis length raises this error, because piecewise fitting would reference nonexistent data.","triggerScenarios":"detrend(x, type='linear', bp=[-2]) or bp=[50] on an axis of length 40; reusing breakpoints computed against a different (longer) dataset or after reshaping.","commonSituations":"Hardcoded breakpoint indices from earlier data; off-by-one after slicing; breakpoints derived from timestamps not sample indices.","solutions":["Clamp/validate breakpoints to 0 <= bp <= x.shape[axis] before calling","Recompute breakpoints from the current data length (indices, not physical units)","Verify the axis argument matches the axis the breakpoints were computed on"],"exampleFix":"// before\njax.scipy.signal.detrend(x, type='linear', bp=bps)\n// after\nN = x.shape[axis]\nbps = [b for b in bps if 0 <= b <= N]\njax.scipy.signal.detrend(x, type='linear', bp=bps)","handlingStrategy":"validation","validationCode":"N = x.shape[axis]\nbp = [b for b in bp if 0 <= b <= N]\nassert len(bp) > 0 or not bp, 'breakpoints filtered'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive breakpoints from x.shape[axis], not hardcoded indices","Recheck breakpoints after slicing or resampling the data"],"tags":["jax","scipy","detrend","breakpoints","index-out-of-range"],"backgroundTag":"index-out-of-bounds","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}