{"id":"d1b5b239cd32d4d1","repo":"pytest-dev/pytest","slug":"expected-an-exception-type-or-a-tuple-of-exception","errorCode":null,"errorMessage":"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. Raising exceptions is already understood as failing the test, so you don't need any special code to say 'this should never raise an exception'.","messagePattern":"Expected an exception type or a tuple of exception types, but got `(.+?)`\\. Raising exceptions is already understood as failing the test, so you don't need any special code to say 'this should never raise an exception'\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/_pytest/raises.py","lineNumber":269,"sourceCode":"        frame) alive until the next cyclic garbage collection run.\n        More detailed information can be found in the official Python\n        documentation for :ref:`the try statement <python:try>`.\n    \"\"\"\n    __tracebackhide__ = True\n\n    if func is None and not args:\n        if set(kwargs) - {\"match\", \"check\", \"expected_exception\"}:\n            msg = \"Unexpected keyword arguments passed to pytest.raises: \"\n            msg += \", \".join(sorted(kwargs))\n            msg += \"\\nUse context-manager form instead?\"\n            raise TypeError(msg)\n\n        if expected_exception is None:\n            return RaisesExc(**kwargs)\n        return RaisesExc(expected_exception, **kwargs)\n\n    if not expected_exception:\n        raise ValueError(\n            f\"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. \"\n            f\"Raising exceptions is already understood as failing the test, so you don't need \"\n            f\"any special code to say 'this should never raise an exception'.\"\n        )\n    if not callable(func):\n        raise TypeError(f\"{func!r} object (type: {type(func)}) must be callable\")\n    with RaisesExc(expected_exception) as excinfo:\n        func(*args, **kwargs)\n    try:\n        return excinfo\n    finally:\n        del excinfo\n\n\n# note: RaisesExc/RaisesGroup uses fail() internally, so this alias\n#  indicates (to [internal] plugins?) that `pytest.raises` will\n#  raise `_pytest.outcomes.Failed`, where\n#  `outcomes.Failed is outcomes.fail.Exception is raises.Exception`","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/raises.py#L251-L287","documentation":"Raised by the function-call form of pytest.raises when the first positional argument (expected_exception) is falsy. pytest already treats any uncaught exception as a test failure, so there is no 'assert it does not raise' mode via raises. Passing None/empty here is a misuse of the API.","triggerScenarios":"Calling pytest.raises(None, my_func), pytest.raises((), my_func), or pytest.raises(0, my_func) — i.e. the function-call form (raises(exc, func)) where exc evaluates to a falsy value. Hits the branch at raises.py:268 (`if not expected_exception:`).","commonSituations":"A parametrized variable intended to hold an exception class is None for some parameter combo; a refactor leaves a stale variable; or a developer misunderstands raises as a 'should not raise' assertion.","solutions":["Remove the pytest.raises wrapper entirely — a test that should not raise simply runs the code directly; an unexpected exception already fails the test.","If you conditionally expect no exception, branch on the parametrized value and only wrap when an exception type is present.","If you need an explicit 'did not raise' assertion, use try/except around the call with pytest.fail('unexpectedly raised') in the except block.","Audit the parametrize table / variable source feeding expected_exception to ensure it is never None or empty when reaching raises."],"exampleFix":"// before\npytest.raises(maybe_none_exc, my_func)\n// after\nif maybe_none_exc is not None:\n    with pytest.raises(maybe_none_exc):\n        my_func()\nelse:\n    my_func()","handlingStrategy":"validation","validationCode":"if not expected_exception:\n    raise ValueError('expected_exception must be a non-empty exception type or tuple')\n# only then:\npytest.raises(expected_exception, func)","typeGuard":"def is_valid_expected(exc) -> bool:\n    return bool(exc) and (\n        isinstance(exc, type) and issubclass(exc, BaseException)\n        or isinstance(exc, tuple) and all(isinstance(e, type) and issubclass(e, BaseException) for e in exc)\n    )","tryCatchPattern":null,"preventionTips":["Never pass None as expected_exception to the function-call form; if 'no exception' is the intent, drop the wrapper.","When parametrizing exception types, include only real exception classes and handle the 'no-expect' case outside raises.","Run a quick truthiness + isinstance check on dynamic expected_exception values before calling raises."],"tags":["pytest","raises","validation","api-misuse"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}