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 .shape for every operand before the failing line, not the line itself.
  • Add keepdims=True when the result must broadcast against the original array.
  • v[:, None] or np.reshape is 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
SymptomLikely causeCheck
A statistic is nanOne NaN in the inputnp.isnan(a).any()
Values look wildly negativeInteger overflow in a narrow dtypea.dtype
Comparison never trueFloat equalitynp.isclose
Totals slightly offAccumulated rounding in float32Compute in float64
axis result is transposedWrong axis collapsedPrint 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.

Views, copies and memory layout Linear algebra with numpy.linalg

Last refreshed 2026-09-18.