{"record":{"id":"577b311e374bdb7c","repo":"pytest-dev/pytest","slug":"exceptions-must-be-derived-from-warning-not-s","errorCode":null,"errorMessage":"exceptions must be derived from Warning, not %s","messagePattern":"exceptions must be derived from Warning, not (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/_pytest/recwarn.py","lineNumber":280,"sourceCode":"\n\n@final\nclass WarningsChecker(WarningsRecorder):\n    def __init__(\n        self,\n        expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning,\n        match_expr: str | re.Pattern[str] | None = None,\n        *,\n        _ispytest: bool = False,\n    ) -> None:\n        check_ispytest(_ispytest)\n        super().__init__(_ispytest=True)\n\n        msg = \"exceptions must be derived from Warning, not %s\"\n        if isinstance(expected_warning, tuple):\n            for exc in expected_warning:\n                if not issubclass(exc, Warning):\n                    raise TypeError(msg % type(exc))\n            expected_warning_tup = expected_warning\n        elif isinstance(expected_warning, type) and issubclass(\n            expected_warning, Warning\n        ):\n            expected_warning_tup = (expected_warning,)\n        else:\n            raise TypeError(msg % type(expected_warning))\n\n        self.expected_warning = expected_warning_tup\n        self.match_expr = match_expr\n\n    def matches(self, warning: warnings.WarningMessage) -> bool:\n        assert self.expected_warning is not None\n        return issubclass(warning.category, self.expected_warning) and bool(\n            self.match_expr is None or re.search(self.match_expr, str(warning.message))\n        )\n\n    def __exit__(","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/pytest-dev/pytest/blob/0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc/src/_pytest/recwarn.py#L262-L298","documentation":"Raised by WarningsChecker.__init__ when an element of an expected_warning tuple is not a subclass of Warning. pytest.warns requires warning categories; passing a non-Warning exception type inside the tuple (e.g. ValueError) is a type confusion. The TypeError uses the same 'exceptions must be derived from Warning' template as the single-type check, interpolating type(exc).","triggerScenarios":"pytest.warns((UserWarning, ValueError)); pytest.warns((DeprecationWarning, RuntimeError)); any tuple where one element fails issubclass(exc, Warning).","commonSituations":"Mixing pytest.raises and pytest.warns argument lists; refactoring a raises tuple into warns; passing exception classes by mistake.","solutions":["Pass only Warning subclasses: pytest.warns((UserWarning, DeprecationWarning)).","If you intended to assert on exceptions, use pytest.raises((ValueError, RuntimeError)).","Validate the tuple before passing: assert all(issubclass(c, Warning) for c in cats)."],"exampleFix":"// before\nwith pytest.warns((UserWarning, ValueError)):\n    ...\n// after\nwith pytest.warns((UserWarning, DeprecationWarning)):\n    ...\n// if exceptions were intended:\nwith pytest.raises(ValueError):\n    ...","handlingStrategy":"type-guard","validationCode":"if isinstance(expected_warning, tuple):\n    assert all(issubclass(c, Warning) for c in expected_warning), 'all tuple elements must be Warning subclasses'","typeGuard":"def warns_tuple_all_warnings(expected_warning) -> bool:\n    if not isinstance(expected_warning, tuple):\n        return True\n    return all(isinstance(c, type) and issubclass(c, Warning) for c in expected_warning)","tryCatchPattern":null,"preventionTips":["Pass only Warning subclasses inside the tuple to pytest.warns.","Keep raises and warns argument lists separate; do not refactor one into the other blindly.","Add a pre-flight assertion in helpers that build the tuple."],"tags":["pytest","warns","warningschecker","type-validation","misuse"],"backgroundTag":null,"analyzedSha":"0d6fbdeffa57c796123f62f81f7dd370d9b7ecdc","analyzedAt":"2026-08-11T20:52:36.969Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}