Skip to content

Raise IndexError for out of bounds axes - #4484

Open
devangpratap wants to merge 1 commit into
ml-explore:mainfrom
devangpratap:index-error-out-of-bounds-axis
Open

Raise IndexError for out of bounds axes#4484
devangpratap wants to merge 1 commit into
ml-explore:mainfrom
devangpratap:index-error-out-of-bounds-axis

Conversation

@devangpratap

@devangpratap devangpratap commented Sep 9, 2026

Copy link
Copy Markdown
  • ☑️ I understand it is strictly prohibited to use AI to write PR description
  • AI usage disclosure: I used Claude to investigate the inconsistency and draft
    the patch. The direction, the scope calls and the review responses are mine,
    and I verified the result myself: full Python and C++ suites, confirmed the
    new test fails on main, and checked each flipped site is a bounds error
    rather than some other argument error. I am responsible for every line.

Closes #4428.

An out of bounds axis is an indexing error, but most of the axis checks throw
std::invalid_argument, which reaches Python as ValueError. The reduction path
already throws std::out_of_range, so the same mistake gives two different
exceptions depending on which op you call.

>>> x = mx.zeros((2, 3))
>>> mx.sum(x, axis=5)          # IndexError
>>> mx.expand_dims(x, 5)       # ValueError

numpy raises AxisError for both, which subclasses ValueError and IndexError.

What changed

Bounds checks now throw std::out_of_range and reach Python as IndexError.
Most go through normalize_axis_index, which covers 28 call sites on its own.
The rest are ad-hoc checks in ops.cpp, fft.cpp, random.cpp and the vmap
bindings. Errors that are not about bounds stay ValueError, so duplicate axes,
rank mismatches and empty arrays are untouched.

Breaking change

IndexError is not a subclass of ValueError, so code catching ValueError on
a bad axis stops catching it. I raised the options in #4428 and will match
whatever is decided there.

Testing

New test in python/tests/test_ops.py asserting every op agrees on IndexError
for an out of bounds axis. It fails on main. Full Python suite passes with 895
tests, C++ suite with 259 cases and 3530 assertions. Built with
MLX_BUILD_METAL=OFF so the Metal kernels were not compiled locally.
uvx pre-commit run --all is clean.

An out of bounds axis is an indexing error, but most axis checks threw
std::invalid_argument, which nanobind surfaces as ValueError. The
reduction path already threw std::out_of_range (IndexError), so
mx.sum(x, axis=5) and mx.expand_dims(x, 5) disagreed on the same
mistake. numpy raises AxisError for both, which subclasses ValueError
and IndexError.

Switch the bounds checks to std::out_of_range. Most of them go through
normalize_axis_index, the rest are ad hoc checks in ops, fft, random
and the vmap bindings. Errors that are not about bounds, duplicate
axes, axis count mismatches and shape mismatches, stay ValueError.

Closes ml-explore#4428
@simeetnayan81

Copy link
Copy Markdown

PR goes against the contributing guidelines related to AI usage policy. (Description is AI generated).

This PRs bring up some breaking changes. Ideally I'd prefer waiting for discussion or solution review in original thread before going for changes directly.

@zcbenz

zcbenz commented Sep 9, 2026

Copy link
Copy Markdown
Member

@ev-br Can you please check if this would be expected change for Array API compatibility?

@ev-br

ev-br commented Sep 10, 2026

Copy link
Copy Markdown

The Array API spec rarely mandates a specific exception type. A typical requirement is If provided an invalid axis, the function must raise an exception. (e.g. moveaxis, concat, repeat).

One relatively rare exception is expand_dims, which additionally mandates that If provided an invalid axis, an IndexError should be raised. --- note the should not must.

Therefore, changing ValueErrors into IndexErrors is a step towards Array API compatibility indeed, even if not a critical one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH]: Using IndexError instead of ValueError at appropriate places?

4 participants