{"record":{"id":"af23743444ccb4f2","repo":"pytest-dev/pytest","slug":"expected-value-must-support-abs-when-relative","errorCode":null,"errorMessage":"expected value must support abs(...) when relative tolerance is used, got {type(expected).__name__}","messagePattern":"expected value must support abs\\(\\.\\.\\.\\) when relative tolerance is used, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/approx.py","lineNumber":455,"sourceCode":"    rel: float | Decimal | None\n    abs: float | Decimal | None\n\n    def __init__(\n        self,\n        expected: ExpectedT,\n        rel: float | Decimal | timedelta | None,\n        abs: float | Decimal | timedelta | None,\n        nan_ok: bool,\n    ) -> None:\n        __tracebackhide__ = True\n        if rel is not None:\n            if not isinstance(rel, (int, float, Decimal)):\n                raise TypeError(\n                    f\"relative tolerance for a scalar value must be an int, float or Decimal, \"\n                    f\"got {type(rel).__name__}\"\n                )\n            if not isinstance(expected, SupportsAbs):\n                raise TypeError(\n                    f\"expected value must support abs(...) when relative tolerance is used, \"\n                    f\"got {type(expected).__name__}\"\n                )\n        if abs is not None and not isinstance(abs, (int, float, Decimal)):\n            raise TypeError(\n                f\"absolute tolerance for a scalar value must be an int, float or Decimal, \"\n                f\"got {type(abs).__name__}\"\n            )\n        super().__init__(expected, rel=rel, abs=abs, nan_ok=nan_ok)\n\n    def __repr__(self) -> str:\n        \"\"\"Return a string communicating both the expected value and the\n        tolerance for the comparison being made.\n\n        For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``.\n        \"\"\"\n        # Don't show a tolerance for values that aren't compared using\n        # tolerances, i.e. non-numerics and infinities. Need to call abs to","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/approx.py#L437-L473","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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))."],"exampleFix":"# before\napprox('3.14', rel=0.01)\n# after\napprox(float('3.14'), rel=0.01)","handlingStrategy":"type-guard","validationCode":"from typing import SupportsAbs\ndef expected_supports_abs(expected) -> bool:\n    return isinstance(expected, SupportsAbs)\n# usage\nif rel is not None and not expected_supports_abs(expected):\n    raise TypeError('expected must support abs() when rel is set; dropping rel for non-numeric')\nassert actual == approx(expected, rel=rel if expected_supports_abs(expected) else None)","typeGuard":"from typing import SupportsAbs\ndef supports_abs(v) -> bool:\n    return isinstance(v, SupportsAbs) and hasattr(v, '__abs__')","tryCatchPattern":null,"preventionTips":["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."],"tags":["pytest","approx","tolerance","type-validation","supports-abs"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}