{"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/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/recwarn.py#L262-L298","documentation":"In WarningsChecker, every element of a tuple passed as expected_warning must be a subclass of Warning. Passing a non-Warning type (e.g. an exception class) in the tuple is rejected.","triggerScenarios":"Calling pytest.warns((UserWarning, ValueError)) — ValueError is an exception, not a Warning. Hits recwarn.py:280 inside the tuple loop.","commonSituations":"Mixing exception classes with warning classes; typo; assuming warns accepts exception types like raises does.","solutions":["Use only Warning subclasses in the tuple: pytest.warns((UserWarning, RuntimeWarning)).","Verify each element with issubclass(x, Warning) before passing when constructed dynamically.","Remember warns is for warnings (Warning subclasses), raises is for exceptions (BaseException subclasses)."],"exampleFix":"// before\npytest.warns((UserWarning, ValueError))\n// after\npytest.warns((UserWarning, RuntimeWarning))","handlingStrategy":"type-guard","validationCode":"for exc in expected_warning:\n    if not (isinstance(exc, type) and issubclass(exc, Warning)):\n        raise TypeError(f'{exc!r} is not a Warning subclass')\npytest.warns(expected_warning)","typeGuard":"def all_are_warning_types(excs) -> TypeGuard[tuple[type[Warning], ...]]:\n    return isinstance(excs, tuple) and all(isinstance(e, type) and issubclass(e, Warning) for e in excs)","tryCatchPattern":null,"preventionTips":["Use only Warning subclasses (UserWarning, DeprecationWarning, etc.) in warns tuples.","Validate each tuple element with issubclass(x, Warning) for dynamic construction.","Remember: warns takes Warning subclasses; raises takes BaseException subclasses."],"tags":["pytest","warns","validation","type-check"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}