Skip to content

Fix Nx.slice crash on scalar tensor#1712

Open
blasphemetheus wants to merge 6 commits intoelixir-nx:mainfrom
blasphemetheus:fork/fix/scalar-slice
Open

Fix Nx.slice crash on scalar tensor#1712
blasphemetheus wants to merge 6 commits intoelixir-nx:mainfrom
blasphemetheus:fork/fix/scalar-slice

Conversation

@blasphemetheus
Copy link
Contributor

@blasphemetheus blasphemetheus commented Mar 20, 2026

If you run Nx.slice on a scalar tensor [tensor with no dimensions, just a single number wrapped in a tensor struct] (aka rank 0), it throws an error

Nx.slice(Nx.tensor(42), [], [])
# ** (ArgError) 1st argument: not a nonempty list
#      :erlang.hd([])
...

Slicing a scalar is a valid no-op. Nx.Shape.slice does it fine. But here BinaryBackend.bin_slice/7 calls hd(strides) on the empty strides list [], which crashes.

The fix is to add a scalar guard clause that matches when all lists are empty and returns the data unchanged.

running scalar_slice_test.exs on main

     Nx.ScalarSliceTest [test/nx/scalar_slice_test.exs]                                 
       * test slice of scalar tensor returns scalar [L#4]                               
       * test slice of scalar tensor returns scalar (20.5ms) [L#4]                      
                                                                                        
       1) test slice of scalar tensor returns scalar (Nx.ScalarSliceTest)               
          test/nx/scalar_slice_test.exs:4                                               
          ** (ArgumentError) errors were found at the given arguments:                  
                                                                                        
            * 1st argument: not a nonempty list                                         
                                                                                        
          code: result = Nx.slice(t, [], [])                                            
          stacktrace:                                                                   
            :erlang.hd([])                                                              
            (nx 0.11.0) lib/nx/binary_backend.ex:1855: Nx.BinaryBackend.bin_slice/7     
            (nx 0.11.0) lib/nx/binary_backend.ex:1848: Nx.BinaryBackend.slice/5         
            (nx 0.11.0) lib/nx.ex:13738: Nx.slice/4                                     
            test/nx/scalar_slice_test.exs:6: (test)                                     
...

originally part of the closed fuzz test edge case PR #1707

bin_slice/7 called hd([]) on empty strides list for rank-0 tensors.
Added scalar guard clause that returns data unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
result = Nx.slice(t, [], [])
assert_in_delta Nx.to_number(result), 3.14, 1.0e-10
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a separate module, let's add a function to nx_test.exs. I would also add a doctest that says:

Sliding a one-dimensional tensor is a no-op:

iex> Nx.slice(42, [], [])

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good! I'll go through and apply that idea to the other PRs too, just adding em at the end of nx_test.exs

Copy link
Contributor

@polvalente polvalente left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should support slicing scalars.
Furthermore, if we're adding support for this, we should also check how EXLA and Torchx behave.

I think the correct PR here is to fail on scalar slicing

@josevalim
Copy link
Contributor

Slicing a scalar could always be a no-op if you pass no dimensions (and raise if you pass any) so there is nothing to be implemented on the other backends because there is no operation.

@josevalim
Copy link
Contributor

FWIW:

import numpy as np
x = np.array(5)
x[()]  # returns 5

But the current implementation requires indeed each backend to implement it (but the doctest should test them aleady)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants