pytest-dev/pytest · error · TypeError
expected value must support abs(...) when relative…
Error message
expected value must support abs(...) when relative tolerance is used, got {type(expected).__name__} What it means
When a relative tolerance is supplied to ApproxScalar, the expected value must support abs() (typing.SupportsAbs) because relative tolerance is computed as rel * abs(expected). pytest rejects non-numeric expected values (strings, None, custom objects without __abs__) up front rather than failing later inside the tolerance property. Without a rel argument this check is skipped and approx falls back to strict equality for non-numeric types.
Solutions
- Drop the rel argument when the expected value is non-numeric: `approx(expected)` uses strict equality.
- Convert the expected value to a numeric type before passing: `approx(float(expected), rel=0.01)`.
- Branch in helper code: only pass rel when isinstance(expected, (int, float, Decimal)).
Example fix
# before
approx('3.14', rel=0.01)
# after
approx(float('3.14'), rel=0.01) Defensive patterns
Strategy: type-guard
Validate before calling
from typing import SupportsAbs
def expected_supports_abs(expected) -> bool:
return isinstance(expected, SupportsAbs)
# usage
if rel is not None and not expected_supports_abs(expected):
raise TypeError('expected must support abs() when rel is set; dropping rel for non-numeric')
assert actual == approx(expected, rel=rel if expected_supports_abs(expected) else None) Type guard
from typing import SupportsAbs
def supports_abs(v) -> bool:
return isinstance(v, SupportsAbs) and hasattr(v, '__abs__') Prevention
- Drop rel for non-numeric expected values (strict equality fallback).
- In generic helpers, branch on isinstance(expected, (int, float, Decimal)) before passing rel.
- Convert string numeric data to float before constructing approx with rel.
When it happens
Trigger: Calling approx('somestring', rel=0.1), approx(None, rel=0.1), approx(SomeObj(), rel=0.1). Triggered at ApproxScalar.__init__ (src/_pytest/approx.py:454-458). Note: bool passes SupportsAbs but is treated as non-numeric elsewhere.
Common situations: Using a tolerance on a value loaded from JSON as a string; comparing identifiers or labels but accidentally passing rel; generic helpers that always pass rel regardless of value type.
Related errors
- absolute tolerance for a scalar value must be an int, float…
- relative tolerance for a scalar value must be an int, float…
- absolute tolerance can't be NaN.
- absolute tolerance can't be negative
- pytest.approx() does not support relative tolerance for…
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/af23743444ccb4f2.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/approx.py:455
rel: float | Decimal | None
abs: float | Decimal | None
def __init__(
self,
expected: ExpectedT,
rel: float | Decimal | timedelta | None,
abs: float | Decimal | timedelta | None,
nan_ok: bool,
) -> None:
__tracebackhide__ = True
if rel is not None:
if not isinstance(rel, (int, float, Decimal)):
raise TypeError(
f"relative tolerance for a scalar value must be an int, float or Decimal, "
f"got {type(rel).__name__}"
)
if not isinstance(expected, SupportsAbs):
raise TypeError(
f"expected value must support abs(...) when relative tolerance is used, "
f"got {type(expected).__name__}"
)
if abs is not None and not isinstance(abs, (int, float, Decimal)):
raise TypeError(
f"absolute tolerance for a scalar value must be an int, float or Decimal, "
f"got {type(abs).__name__}"
)
super().__init__(expected, rel=rel, abs=abs, nan_ok=nan_ok)
def __repr__(self) -> str:
"""Return a string communicating both the expected value and the
tolerance for the comparison being made.
For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``.
"""
# Don't show a tolerance for values that aren't compared using
# tolerances, i.e. non-numerics and infinities. Need to call abs toView on GitHub (pinned to 0d6fbdeffa)