pytest-dev/pytest · error · ValueError

relative tolerance can't be NaN.

Error message

relative tolerance can't be NaN.

What it means

Raised by ApproxScalar.tolerance when the computed relative tolerance is NaN. This happens when rel is NaN or when abs(expected) is NaN (i.e. the expected value itself is NaN), since the product becomes NaN.

Source

Thrown at src/_pytest/approx.py:589

                return absolute_tolerance

        # Figure out what the relative tolerance should be.  ``self.rel`` is
        # either None or a value specified by the user.  This is done after
        # we've made sure the user didn't ask for an absolute tolerance only,
        # because we don't want to raise errors about the relative tolerance if
        # we aren't even going to use it.
        rel = self.rel if self.rel is not None else self.DEFAULT_RELATIVE_TOLERANCE
        # expected is SupportAbs, checked in __init__.
        # The typing here is not exact...
        abs_expected: ExpectedT = abs(self.expected)  # type: ignore[arg-type]
        relative_tolerance: float | Decimal = rel * abs_expected  # type: ignore[operator]

        if relative_tolerance < 0:
            raise ValueError(
                f"relative tolerance can't be negative: {relative_tolerance}"
            )
        if math.isnan(relative_tolerance):
            raise ValueError("relative tolerance can't be NaN.")

        # Return the larger of the relative and absolute tolerances.
        return max(relative_tolerance, absolute_tolerance)


class ApproxDecimal(ApproxScalar[Decimal]):
    """Perform approximate comparisons where the expected value is a Decimal."""

    DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
    DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
    rel: Decimal | None
    abs: Decimal | None

    def __init__(
        self,
        expected: Decimal,
        rel: float | Decimal | timedelta | None,
        abs: float | Decimal | timedelta | None,

View on GitHub (pinned to 98b357f69e)

Solutions

  1. If NaN is a valid expected value, pass nan_ok=True: approx(float('nan'), nan_ok=True).
  2. Ensure the expected value is a finite number before passing it to approx().
  3. Sanitize rel so it is never NaN.

Example fix

// before
assert v == approx(float('nan'))
// after
assert v == approx(float('nan'), nan_ok=True)
Defensive patterns

Strategy: validation

Validate before calling

import math

def safe_approx(x, rel=None, abs=None, nan_ok=False):
    if isinstance(x, float) and math.isnan(x) and not nan_ok:
        # compare NaN explicitly rather than via approx
        return _NaNApprox()
    if rel is not None and (isinstance(rel, float) and math.isnan(rel)):
        raise ValueError('rel is NaN')
    return pytest.approx(x, rel=rel, abs=abs, nan_ok=nan_ok)

class _NaNApprox:
    def __eq__(self, other): return isinstance(other, float) and math.isnan(other)\n    def __repr__(self): return 'approx(nan)'

Type guard

import math

def may_contain_nan(value) -> bool:
    try:
        return math.isnan(value)
    except TypeError:
        return False

Try / catch

try:
    assert v == pytest.approx(expected)
except ValueError as e:
    if 'NaN' in str(e) and math.isnan(expected):
        assert math.isnan(v)
    else:
        raise

Prevention

When it happens

Trigger: Call pytest.approx(float('nan')) and compare; or pass rel=float('nan'). Either makes rel*abs(expected) NaN and trips this check.

Common situations: Comparing a value that may legitimately be NaN without setting nan_ok=True; expected value read from a CSV/array that contains NaN; rel tolerance derived from a NaN-containing computation.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/e10b63e3e0116544.json. Report an issue: GitHub.