pytest-dev/pytest · error · TypeError

expected value must support abs(...) when relative tolerance

Error message

expected value must support abs(...) when relative tolerance is used, got {type(expected).__name__}

What it means

When a relative tolerance (rel) is specified for ApproxScalar, the expected value must support abs(...) to compute the relative component (rel * abs(expected)). If the expected value does not implement __abs__ (e.g., a string or custom object), pytest raises TypeError. This is validated eagerly in __init__.

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 to

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Use absolute tolerance (abs=) instead of relative tolerance for non-numeric or non-abs-supporting values.
  2. Remove the rel parameter if only exact comparison is needed.
  3. Ensure the expected value is numeric (int, float, Decimal) when using rel.

Example fix

# before
approx('hello', rel=0.01)

# after
approx('hello')  # no rel, exact comparison
# or for numeric values:
approx(3.14, rel=0.01)
Defensive patterns

Strategy: type-guard

Validate before calling

from typing import Protocol
import numbers

class SupportsAbs(Protocol):
    def __abs__(self) -> float: ...

def validate_expected_with_rel(expected, rel):
    if rel is not None and not hasattr(expected, '__abs__'):
        raise TypeError(f'expected value must support abs() when rel is used, got {type(expected).__name__}')
    return expected

# usage:
# if rel_tol: validate_expected_with_rel(value, rel_tol)

Type guard

def supports_abs(value) -> bool:
    return hasattr(value, '__abs__') and callable(getattr(value, '__abs__'))

Prevention

When it happens

Trigger: Calling approx('hello', rel=0.01) or approx(SomeObj(), rel=0.01) where the expected value lacks __abs__. The isinstance(expected, SupportsAbs) check fails.

Common situations: Comparing a non-numeric value with a relative tolerance, or passing a tolerance meant for numbers to a custom object comparison.

Related errors


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