pytest-dev/pytest · error · ValueError
Expected , but got
Error message
Expected {expected}, but got {exc.__name__!r} What it means
Raised by RaisesExc._parse_exc when the exc argument is a class (isinstance(exc, type) is True) but it is neither a BaseException subclass nor a validly-parameterized exception group. It is a defensive ValueError for a path the static type checker marks unreachable — reached only when callers bypass type hints. The message interpolates expected (the human label passed by the caller, e.g. 'a BaseException type') and the offending class name.
Solutions
- Pass the actual exception class that derives from BaseException, e.g. pytest.raises(ValueError).
- If the value is a group-bearing generic, use the bare group class or RaisesGroup instead of a constrained generic.
- Add a runtime assertion before the call: assert isinstance(exc, type) and issubclass(exc, BaseException).
Example fix
// before
with pytest.raises(MyErrorCode): # MyErrorCode is an IntEnum
...
// after
with pytest.raises(MyException):
...
// or guard:
assert issubclass(exc_target, BaseException)
with pytest.raises(exc_target):
... Defensive patterns
Strategy: type-guard
Validate before calling
assert isinstance(exc, type) and issubclass(exc, BaseException), f'expected a BaseException type, got {exc!r}' Type guard
def is_exception_type(exc) -> bool:
return isinstance(exc, type) and issubclass(exc, BaseException) Prevention
- Always pass exception classes derived from BaseException to raises/RaisesExc.
- When computing the exception type dynamically, assert it is a BaseException subclass before use.
- Run mypy/pyright in strict mode so the type checker catches misuse before runtime.
When it happens
Trigger: Passing a non-exception class such as pytest.raises(int), pytest.raises(dict), or a dataclass type to raises/RaisesExc. Reached only after the earlier BaseException-group branch fails, when the value is a type but issubclass(exc, BaseException) is False.
Common situations: Passing a Pydantic/custom error-code class instead of its raised exception type; refactoring that accidentally substitutes an enum or sentinel class for the exception type; dynamically computed exception arguments that lose their exception-ness.
Related errors
- exceptions must be derived from Warning, not
- Expected , but got an exception instance
- You must specify at least one parameter to match on.
- absolute tolerance for a scalar value must be an int, float…
- `allow_unwrapped=True` bypasses the `match` and `check`…
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/330548c3671ec7dd.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/raises.py:451
) or (
issubclass(origin_exc, BaseExceptionGroup)
and exc_type in (BaseException, Any)
):
if not issubclass(origin_exc, ExceptionGroup):
self.is_baseexception = True
return cast(type[BaseExcT_1], origin_exc)
else:
raise ValueError(
f"Only `ExceptionGroup[Exception]` or `BaseExceptionGroup[BaseException]` "
f"are accepted as generic types but got `{exc}`. "
f"As `raises` will catch all instances of the specified group regardless of the "
f"generic argument specific nested exceptions has to be checked "
f"with `RaisesGroup`."
)
# unclear if the Type/ValueError distinction is even helpful here
msg = f"Expected {expected}, but got "
if isinstance(exc, type): # type: ignore[unreachable]
raise ValueError(msg + f"{exc.__name__!r}")
if isinstance(exc, BaseException): # type: ignore[unreachable]
raise TypeError(msg + f"an exception instance: {type(exc).__name__}")
raise TypeError(msg + repr(type(exc).__name__))
@property
def fail_reason(self) -> str | None:
"""Set after a call to :meth:`matches` to give a human-readable reason for why the match failed.
When used as a context manager the string will be printed as the reason for the
test failing."""
return self._fail_reason
def _check_check(
self: AbstractRaises[BaseExcT_1],
exception: BaseExcT_1,
) -> bool:
if self.check is None:
return True
View on GitHub (pinned to 0d6fbdeffa)