pytest-dev/pytest · error · ValueError
Only `ExceptionGroup[Exception]` or…
Error message
Only `ExceptionGroup[Exception]` or `BaseExceptionGroup[BaseException]` are accepted as generic types but got `{exc}`. As `raises` will catch all instances of the specified group regardless of the generic argument specific nested exceptions has to be checked with `RaisesGroup`. What it means
Raised by RaisesExc._parse_exc when a parameterized exception group is passed to pytest.raises (or RaisesExc) with a generic argument that is not exactly Exception, BaseException, or Any. pytest intentionally rejects constrained generics like ExceptionGroup[ValueError] because pytest.raises catches the whole group regardless of the type argument, so the nested type would be silently ignored and produce misleading tests. Use RaisesGroup to assert on nested exception types.
Solutions
- Replace pytest.raises(ExceptionGroup[ValueError]) with pytest.raises(ExceptionGroup) wrapped in or followed by a RaisesGroup(ValueError) assertion to inspect nested exceptions.
- Use RaisesGroup(ValueError) directly as the raises argument to match a group whose leaf is ValueError.
- If you truly want to catch any ExceptionGroup, pass ExceptionGroup (bare) or ExceptionGroup[Any].
Example fix
// before
with pytest.raises(ExceptionGroup[ValueError]):
...
// after
with pytest.raises(RaisesGroup(ValueError)):
...
// or for the outer group catch plus inner check:
with pytest.raises(ExceptionGroup) as eg:
...
assert RaisesGroup(ValueError).matches(eg.value) Defensive patterns
Strategy: validation
Validate before calling
import typing
origin = typing.get_origin(exc)
if origin is not None and issubclass(origin, BaseExceptionGroup):
arg = typing.get_args(exc)[0]
assert arg in (Exception, BaseException, typing.Any), (
f'pytest.raises cannot constrain {origin.__name__}[{arg.__name__}]; '
'use RaisesGroup for nested-type matching'
) Type guard
def is_valid_raises_group_generic(exc) -> bool:
import typing
origin = typing.get_origin(exc)
if origin is None or not issubclass(origin, BaseExceptionGroup):
return True
arg = typing.get_args(exc)[0]
return arg in (Exception, BaseException, typing.Any) Prevention
- Treat pytest.raises group generics as catch-all; use RaisesGroup for nested-type assertions.
- Add a lint rule or pre-commit check flagging raises(ExceptionGroup[<concrete>]).
- Document the convention in your test helpers.
When it happens
Trigger: Calling pytest.raises(ExceptionGroup[ValueError]), pytest.raises(BaseExceptionGroup[KeyboardInterrupt]), or RaisesExc(ExceptionGroup[KeyError]) — any BaseExceptionGroup subclass parameterized with a concrete non-Any, non-base exception type. The branch fires after get_origin(exc) returns a BaseExceptionGroup subclass and get_args(exc)[0] fails the Exception/BaseException/Any membership check.
Common situations: Developers new to Python 3.11 exception groups who try to assert 'a group containing ValueError' with the natural-looking raises(ExceptionGroup[ValueError]) syntax. Migrating from raises(ValueError) to group-based assertions and assuming the type parameter expresses the inner expectation.
Related errors
- `allow_unwrapped=True` bypasses the `match` and `check`…
- `allow_unwrapped=True` has no effect when expecting a…
- Expected , but got . RaisesGroup does not support tuples of…
- You cannot specify a nested structure inside a RaisesGroup…
- You cannot specify multiple exceptions with…
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/be424c7d64acc5bb.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/raises.py:441
if not issubclass(exc, Exception):
self.is_baseexception = True
return exc
# because RaisesGroup does not support variable number of exceptions there's
# still a use for RaisesExc(ExceptionGroup[Exception]).
origin_exc: type[BaseException] | None = get_origin(exc)
if origin_exc and issubclass(origin_exc, BaseExceptionGroup):
exc_type = get_args(exc)[0]
if (
issubclass(origin_exc, ExceptionGroup) and exc_type in (Exception, Any)
) 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 theView on GitHub (pinned to 0d6fbdeffa)