Debugging array code: shape errors and NumPy 2 pitfalls
Read broadcast errors without guessing, find the axis bug, and avoid the numeric and API traps that NumPy 2 introduced.
Reading broadcast and axis errors
The message operands could not be broadcast together with shapes (3,4) (4,3) is a complete diagnosis if you read it the right way: NumPy aligns shapes from the right and finds 4 against 3 in the last position. Fix the shape, not the operator.
a = np.ones((3, 4))
b = np.ones((4, 3))
a + b # ValueError: shapes (3,4) and (4,3) are not aligned
(a + b.T).shape # fix by transposing
a + np.ones((3,)) # fails: compare 4 with 3
v = np.ones(3)
a + v[:, None] # (3,1) broadcasts against (3,4): rows
# axis bugs: check the resulting shape, do not assume
a.sum(axis=1).shape # (3,) - one value per row
a.sum(axis=1, keepdims=True).shape # (3,1) - keeps the axis for broadcasting- Print
.shapefor every operand before the failing line, not the line itself. - Add
keepdims=Truewhen the result must broadcast against the original array. v[:, None]ornp.reshapeis almost always the fix for a shape mismatch.- A result of shape (4, 3) where you expected (3, 4) means an operand was transposed earlier.
Numeric traps
np.array([127], dtype=np.int8) + 1 # -128: integer overflow wraps
np.array([100], dtype=np.int8) + 100 # also wraps, no error
0.1 + 0.2 == 0.3 # False
np.isclose(0.1 + 0.2, 0.3) # True
np.allclose(A, B, rtol=1e-5, atol=1e-8)
x = np.array([1.0, np.nan, 3.0])
x == x # array([True, False, True]) - NaN is never equal
np.isnan(x).sum() # 1
np.isfinite(x).all()
np.array([1, 2, 3]).mean() # 2.0 - an integer array promoted to float
np.int64(2**62) * 4 # overflow: check the dtype before big products| Symptom | Likely cause | Check |
|---|---|---|
A statistic is nan | One NaN in the input | np.isnan(a).any() |
| Values look wildly negative | Integer overflow in a narrow dtype | a.dtype |
| Comparison never true | Float equality | np.isclose |
| Totals slightly off | Accumulated rounding in float32 | Compute in float64 |
axis result is transposed | Wrong axis collapsed | Print the shape |
What changed in NumPy 2
np.array(x, copy=False) # NumPy 2: raises if a copy is needed
np.asarray(x) # the portable way to say "no copy if possible"
np.array(x, copy=None) # explicit: copy only when required
np.float_ # removed -> np.float64
np.NaN, np.Inf # removed -> np.nan, np.inf
np.trapz(y, x) # renamed -> np.trapezoid(y, x)
a.ptp() # removed -> np.ptp(a)
print(np.float64(3.0)) # NumPy 2 repr: np.float64(3.0) rather than 3.0⚠️
NumPy 2 changed dtype promotion (NEP 50): a Python
float no longer forces a float64 result, so float32_array + 1.5 stays float32. Pin your NumPy version and re-run your test suite after upgrading rather than trusting that results are bit-identical.FAQ
My array is the right shape but the numbers are scrambled. Why?
Almost always a view whose strides came from a transpose or a column slice, combined with code that assumed contiguous memory. Copy with
np.ascontiguousarray at the boundary, or reorder the operation.How do I upgrade to NumPy 2 safely?
Install
numpy>=2 in a branch, run the test suite, and grep for the removed aliases listed above. Most failures come from copy=False and from float32 results no longer being widened.Related
Views, copies and memory layout Linear algebra with numpy.linalg
Last refreshed 2026-09-18.