ValueError: object __array__ method not producing an array in NumPy
Ensure custom classes implementing __array__() return a valid np.ndarray instance rather than primitive scalars, strings, or uncoerced lists.
Root Cause Analysis
This error occurs when Python tries to coerce a custom object into a NumPy ndarray via the __array__ protocol, but the object's __array__ method returns a value that is not an instance of numpy.ndarray.
1. The NumPy __array__ Protocol Contract
NumPy provides the __array__ special method to allow third-party classes, custom data wrappers, and mathematical containers to seamlessly integrate with NumPy functions like np.asarray(), np.array(), and mathematical ufuncs. According to the NumPy protocol specification, any implementation of __array__(self, dtype=None, copy=None) must return an authentic np.ndarray.
2. Returning Primitive Types or Strings
A frequent bug occurs when developers return raw Python primitives such as a string, integer, dictionary, or raw list from __array__ assuming that NumPy will automatically convert the return value into an array. When np.asarray() receives a non-ndarray return value from __array__, it halts execution and raises ValueError: object __array__ method not producing an array.
3. Signature Mismatch with Dtype and Copy Arguments
In modern NumPy versions (especially NumPy 1.20+ and 2.0+), NumPy passes dtype and copy keyword arguments to the __array__ method. If the custom method fails to handle these arguments or returns an incompatible data type when dtype is requested, the protocol validation fails.
4. Wrapper Classes and Incomplete Delegation
When wrapping external libraries or building pandas/arrow compatible adapters, delegation bugs where the underlying wrapped buffer is None or uninitialized will cause __array__ to return None or a non-array sentinel, immediately triggering this exception.
Reproduction Code (MCVE)
import numpy as np
class CustomDataWrapper:
def __array__(self, dtype=None, copy=None):
# Bug: returning a string instead of an ndarray
return 'invalid_string_return_value'
wrapper = CustomDataWrapper()
np.asarray(wrapper)
Solution 1: Return a Valid np.ndarray from __array__
Wrap internal sequences or buffers in np.asarray() inside the __array__ implementation, respecting optional dtype and copy parameters.
import numpy as np
class ValidDataWrapper:
def __init__(self, values):
self.values = values
def __array__(self, dtype=None, copy=None):
return np.asarray(self.values, dtype=dtype)
wrapper = ValidDataWrapper([10, 20, 30, 40])
arr = np.asarray(wrapper)
print(f'Successfully converted: {arr}, type: {type(arr)}')
Solution 2: Provide an Explicit to_numpy() Conversion Method
Instead of relying solely on the implicit __array__ protocol, provide an explicit conversion method (to_numpy()) for controlled data transformations.
import numpy as np
class DataContainer:
def __init__(self, items):
self.items = list(items)
def to_numpy(self, dtype=None):
return np.array(self.items, dtype=dtype)
container = DataContainer([1.5, 2.8, 3.9])
arr = container.to_numpy(dtype=np.float64)
print(f'Explicit conversion array: {arr}, dtype: {arr.dtype}')
A common mistake when implementing custom array containers is returning a standard Python list from __array__. While Python lists are sequences, NumPy requires an actual np.ndarray instance to be returned by __array__. Always wrap raw lists in np.asarray(self.data, dtype=dtype). Another critical edge case involves handling optional arguments: modern NumPy passes both dtype and copy keyword arguments. If your method signature is defined as def __array__(self): without *args or dtype=None, copy=None, calls from np.asarray(obj, dtype=float) will fail with a TypeError before even reaching array validation. Contrasting this error with TypeError: Cannot convert numpy.ndarray to numpy.ndarray, the ValueError specifically indicates that the protocol method was found and executed, but violated the strict type contract of the protocol return value.