pytest-dev/pytest · error · ValueError
relative tolerance can't be negative
Error message
relative tolerance can't be negative: {relative_tolerance} What it means
In ApproxScalar.tolerance, after computing relative_tolerance = rel * abs(expected), pytest rejects a negative result. This typically means rel itself was negative, since abs(expected) is non-negative for normal numbers. A negative relative tolerance is meaningless and would break the max() selection logic, so it is rejected with the computed value reported.
Solutions
- Use a positive relative tolerance: `approx(1.0, rel=0.01)`.
- Validate rel before passing: `assert 0 <= rel <= 1`.
- If rel is computed, take abs(rel) once the sign error is fixed upstream.
Example fix
# before approx(value, rel=-0.01) # after approx(value, rel=0.01)
Defensive patterns
Strategy: validation
Validate before calling
def is_nonneg_rel(rel) -> bool:
return rel is None or (isinstance(rel, (int, float)) and rel >= 0)
# usage
if not is_nonneg_rel(rel):
raise ValueError(f'rel must be >= 0, got {rel}')
assert actual == approx(expected, rel=rel) Type guard
def is_nonneg_float(v) -> bool:
return isinstance(v, (int, float)) and v >= 0 Prevention
- Validate 0 <= rel before constructing approx.
- Audit sign errors in computed tolerances.
- Add asserts in parameterized tests that rel stays in [0, 1].
When it happens
Trigger: Calling approx(1.0, rel=-0.01); rel read from config with a stray minus sign; rel computed as a difference that went negative. Triggered at ApproxScalar.tolerance (src/_pytest/approx.py:584-587).
Common situations: Tolerance arithmetic producing negative values; sign errors in parameterized tests; mis-typed negative literal.
Related errors
- absolute tolerance can't be NaN.
- absolute tolerance can't be negative
- pytest.approx() does not support relative tolerance for…
- relative tolerance can't be NaN.
- absolute tolerance for a scalar value must be an int, float…
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/f1206e518f264809.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/approx.py:585
# 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__.
# 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__(View on GitHub (pinned to 0d6fbdeffa)