pytest-dev/pytest · error · ValueError

absolute tolerance can't be negative

Error message

absolute tolerance can't be negative: {absolute_tolerance}

What it means

The ApproxScalar.tolerance property computes the effective absolute tolerance and rejects negative values because a negative tolerance window makes no geometric sense (no value would ever be 'within' a negative distance of expected). pytest raises ValueError naming the offending value so the user can correct it. This is checked before the NaN check.

Solutions

  1. Use a positive absolute tolerance: `approx(1.0, abs=1e-3)`.
  2. If computing tolerance dynamically, clamp with `abs(abs_tol, ...)` or `max(abs_tol, 0.0)` after fixing the sign logic.
  3. Validate config-supplied tolerances at load time and fail with a clearer message.

Example fix

# before
approx(value, abs=-1e-3)
# after
approx(value, abs=1e-3)
Defensive patterns

Strategy: validation

Validate before calling

def is_nonneg_tolerance(t) -> bool:
    return t is None or (isinstance(t, (int, float)) and t >= 0)
# usage
if not is_nonneg_tolerance(abs_tol):
    raise ValueError(f'absolute tolerance must be >= 0, got {abs_tol}')
assert actual == approx(expected, abs=abs_tol)

Type guard

from decimal import Decimal
def is_nonneg_scalar(v) -> bool:
    return isinstance(v, (int, float, Decimal)) and v >= 0

Prevention

When it happens

Trigger: Calling approx(1.0, abs=-1e-3); passing abs=float('-1e-3'); abs computed/loaded as negative due to a sign error or subtraction bug. Triggered at ApproxScalar.tolerance (src/_pytest/approx.py:560-563).

Common situations: Tolerance read from config where a stray minus sign was introduced; arithmetic on tolerances (abs=a-b) that can go negative; copy-paste errors.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/405375e8bb014fbd. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/approx.py:561

        return result

    __hash__ = None

    @property
    def tolerance(self):
        """Return the tolerance for the comparison.

        This could be either an absolute tolerance or a relative tolerance,
        depending on what the user specified or which would be larger.
        """
        # Figure out what the absolute tolerance should be.  ``self.abs`` is
        # either None or a value specified by the user.
        absolute_tolerance = (
            self.abs if self.abs is not None else self.DEFAULT_ABSOLUTE_TOLERANCE
        )

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

        # If the user specified an absolute tolerance but not a relative one,
        # just return the absolute tolerance.
        if self.rel is None:
            if self.abs is not None:
                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__.

View on GitHub (pinned to 0d6fbdeffa)