{"id":"951c302d74f09e9c","repo":"pytest-dev/pytest","slug":"fail-reason","errorCode":null,"errorMessage":"{fail_reason}","messagePattern":"\\{fail_reason\\}","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"src/_pytest/raises.py","lineNumber":711,"sourceCode":"    ) -> bool:\n        __tracebackhide__ = True\n        if exc_type is None:\n            if not self.expected_exceptions:\n                fail(\"DID NOT RAISE any exception\")\n            if len(self.expected_exceptions) == 1:\n                fail(f\"DID NOT RAISE {self.expected_exceptions[0].__name__}\")\n            else:\n                names = \", \".join(x.__name__ for x in self.expected_exceptions)\n                fail(f\"DID NOT RAISE any of ({names})\")\n\n        assert self.excinfo is not None, (\n            \"Internal error - should have been constructed in __enter__\"\n        )\n\n        if not self.matches(exc_val):\n            if self._just_propagate:\n                return False\n            raise AssertionError(self._fail_reason) from None\n\n        # Cast to narrow the exception type now that it's verified....\n        # even though the TypeGuard in self.matches should be narrowing\n        exc_info = cast(\n            \"tuple[type[BaseExcT_co_default], BaseExcT_co_default, types.TracebackType]\",\n            (exc_type, exc_val, exc_tb),\n        )\n        self.excinfo.fill_unfilled(exc_info)\n        return True\n\n\n@final\nclass RaisesGroup(AbstractRaises[BaseExceptionGroup[BaseExcT_co]]):\n    \"\"\"\n    .. versionadded:: 8.4\n\n    Contextmanager for checking for an expected :exc:`ExceptionGroup`.\n    This works similar to :func:`pytest.raises`, but allows for specifying the structure of an :exc:`ExceptionGroup`.","sourceCodeStart":693,"sourceCodeEnd":729,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/raises.py#L693-L729","documentation":"The AssertionError surfaced when an exception WAS raised and its type matched, but it failed the match regex or the check predicate. The message is the dynamically-built fail_reason explaining exactly why the match failed (regex mismatch, check returned False, etc.).","triggerScenarios":"with pytest.raises(ValueError, match='foo'): raise ValueError('bar') — type matches but the regex does not. Or a check=lambda e: e.code==5 that returns False. Hits raises.py:711.","commonSituations":"Regex is too strict or wrong; expected message wording changed in the code under test; the check predicate has a logic bug; special regex characters in the expected literal text are not escaped.","solutions":["Read the fail_reason — it shows the expected regex and the actual message; align the regex to reality.","Use re.escape(literal_text) when matching literal strings that contain regex metacharacters.","Debug the check predicate in isolation to confirm it returns True for the actual exception.","Loosen or correct the match pattern to reflect the current exception message format."],"exampleFix":"// before\nwith pytest.raises(ValueError, match='^foo$'):\n    raise ValueError('bar')\n// after\nwith pytest.raises(ValueError, match='bar'):\n    raise ValueError('bar')","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    with pytest.raises(ValueError, match=pattern):\n        func()\nexcept AssertionError as e:\n    # inspect e.args[0] for the fail_reason, then fix pattern or check\n    raise","preventionTips":["Use re.escape(literal_text) for match patterns containing regex metacharacters.","Keep match patterns loose (substring search) unless you need full anchoring with ^...$.","Unit-test check predicates in isolation against representative exception instances.","Update match patterns whenever the exception message format changes in the code under test."],"tags":["pytest","raises","assertion","regex"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}