pytest-dev/pytest · error · TypeError

absolute tolerance for a scalar value must be an int, float…

Error message

absolute tolerance for a scalar value must be an int, float or Decimal, got {type(abs).__name__}

What it means

ApproxScalar.__init__ validates that an absolute tolerance (abs) must be an int, float, or Decimal. Other types (string, timedelta, list) are rejected with the offending type name. This mirrors the rel validation and fails fast because abs is later compared numerically in the tolerance property.

Solutions

  1. Pass abs as a plain float: `approx(1.0, abs=1e-3)`.
  2. Cast config-supplied values: `abs=float(cfg['abs'])`.
  3. For Decimal expected, pass Decimal abs: `approx(Decimal('1'), abs=Decimal('0.001'))`.

Example fix

# before
approx(1.0, abs=timedelta(seconds=1))
# after
approx(1.0, abs=1e-3)
Defensive patterns

Strategy: validation

Validate before calling

from decimal import Decimal
def is_valid_abs(abs_tol) -> bool:
    return abs_tol is None or isinstance(abs_tol, (int, float, Decimal))
# usage
abs_tol = float(config['abs']) if isinstance(config['abs'], str) else config['abs']
assert is_valid_abs(abs_tol)
assert actual == approx(expected, abs=abs_tol)

Type guard

from decimal import Decimal
def is_scalar_abs(v) -> bool:
    return v is None or isinstance(v, (int, float, Decimal))

Prevention

When it happens

Trigger: Passing abs='0.001', abs=timedelta(seconds=1) to a scalar (non-datetime) approx, abs=[0.1], abs=None-as-string. Triggered at ApproxScalar.__init__ (src/_pytest/approx.py:459-463). Note: timedelta is valid only for datetime/timedelta expected values (handled by ApproxTimedelta), not for scalars.

Common situations: Copying abs=timedelta(...) syntax from datetime examples into numeric comparisons; loading tolerance from config as a string; passing a numpy float (accepted since it subclasses Python float via __class__).

Related errors


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

Appendix: source

Thrown at src/_pytest/approx.py:460

        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
        # handle complex numbers, e.g. (inf + 1j).
        if (
            _is_bool(self.expected)
            or (not isinstance(self.expected, Complex | Decimal))
            or math.isinf(abs(self.expected))

View on GitHub (pinned to 0d6fbdeffa)