{"record":{"id":"88bc1b7921b8c8b3","repo":"jax-ml/jax","slug":"duplicate-value-in-axis-axis","errorCode":null,"errorMessage":"duplicate value in 'axis': {axis}","messagePattern":"duplicate value in 'axis': (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/numpy/reductions.py","lineNumber":180,"sourceCode":"    result = op(initial_arr, result)\n  if keepdims:\n    result = lax.expand_dims(result, pos_dims)\n  return lax.convert_element_type(result, dtype or result_dtype)\n\ndef _canonicalize_axis_allow_named(x, rank):\n  return maybe_named_axis(x, lambda i: canonicalize_axis(i, rank), lambda name: name)\n\ndef _reduction_dims(a: ArrayLike, axis: Axis):\n  if axis is None:\n    return (tuple(range(np.ndim(a))),) * 2\n  if not isinstance(axis, (np.ndarray, tuple, list)):\n    axes = (axis,)\n  else:\n    axes = axis\n  canon_axis = tuple(_canonicalize_axis_allow_named(x, np.ndim(a))\n                     for x in axes)\n  if len(canon_axis) != len(set(canon_axis)):\n    raise ValueError(f\"duplicate value in 'axis': {axis}\")\n  canon_pos_axis = tuple(x for x in canon_axis if isinstance(x, int))\n  if len(canon_pos_axis) != len(canon_axis):\n    return canon_pos_axis, canon_axis\n  else:\n    return canon_axis, canon_axis\n\ndef _reduction_init_val(a: Array, init_val: Any) -> np.ndarray:\n  # This function uses np.* functions because lax pattern matches against the\n  # specific concrete values of the reduction inputs. TypedNdArray prevents\n  # canonicalization when explicit 64-bit dtypes are allowed.\n  a_dtype = a.dtype\n  if a_dtype == 'bool':\n    return literals.TypedNdArray(np.array(init_val > 0, dtype=a_dtype))\n  if (np.isinf(init_val) and dtypes.issubdtype(a_dtype, np.floating)\n      and not dtypes.supports_inf(a_dtype)):\n    init_val = np.array(dtypes.finfo(a_dtype).min if np.isneginf(init_val)\n                        else dtypes.finfo(a_dtype).max, dtype=a_dtype)\n  try:","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/numpy/reductions.py#L162-L198","documentation":"The axis argument to a reduction contained the same axis more than once after canonicalization (including negative indices resolving to the same axis, or duplicated names), which numpy also rejects.","triggerScenarios":"jnp.sum(x, axis=(0, 0)), jnp.sum(x, axis=(0, -2)) on a 2-D+ array, or axis=('batch', 0) where 'batch' canonicalizes to 0.","commonSituations":"Programmatically building axis tuples that concatenate ranges without deduplication; handling negative indices alongside positive ones (axis=(1, -1) on 2-D input).","solutions":["Deduplicate axes before calling: axis=tuple(set(canonical_axes))","Normalize negative indices first, then dedupe","Validate the axis tuple with a helper before the reduction"],"exampleFix":"// before\njnp.sum(x, axis=(0, 0, 1))\n// after\naxes = tuple(dict.fromkeys(ax % x.ndim for ax in (0, 0, 1)))\njnp.sum(x, axis=axes)","handlingStrategy":"validation","validationCode":"axes = tuple(dict.fromkeys(a % x.ndim for a in axis_tuple))  # dedupe after normalizing negatives\njnp.sum(x, axis=axes)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Deduplicate axis tuples built from concatenated ranges","Normalize negative indices before combining"],"tags":["jax","reductions","axis","duplicate"],"backgroundTag":"duplicate-axis-argument","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}