pytest-dev/pytest · error · ValueError

You cannot specify multiple exceptions with…

Error message

You cannot specify multiple exceptions with `allow_unwrapped=True.` If you want to match one of multiple possible exceptions you should use a `RaisesExc`. E.g. `RaisesExc(check=lambda e: isinstance(e, (...)))`

What it means

Raised by RaisesGroup.__init__ when allow_unwrapped=True is combined with more than one expected exception (the other_exceptions branch). allow_unwrapped means the matcher will accept a single non-group exception by wrapping it implicitly; with multiple candidate types that single exception could not be disambiguated. pytest steers users to RaisesExc(check=lambda e: isinstance(e, (...))) for 'one of several' matching.

Solutions

  1. Drop allow_unwrapped and let the exception arrive already wrapped in an ExceptionGroup.
  2. Keep allow_unwrapped=True but expect a single type, and move multi-type matching into a check: RaisesExc(check=lambda e: isinstance(e, (ValueError, KeyError))).
  3. Wrap the group matcher so it accepts either shape via a custom check on RaisesGroup itself.

Example fix

// before
with pytest.raises(RaisesGroup(ValueError, KeyError, allow_unwrapped=True)):
    ...
// after
with pytest.raises(RaisesExc(check=lambda e: isinstance(e, (ValueError, KeyError)))):
    ...
Defensive patterns

Strategy: validation

Validate before calling

n = 0 if expected_exception is None else (len(expected_exception) if isinstance(expected_exception, tuple) else 1)
n += len(other_exceptions)
assert not (allow_unwrapped and n > 1), 'allow_unwrapped=True requires exactly one expected exception'

Type guard

def raisesgroup_unwrapped_single(expected_exception, other_exceptions, allow_unwrapped) -> bool:
    n = 0 if expected_exception is None else (len(expected_exception) if isinstance(expected_exception, tuple) else 1)
    n += len(other_exceptions or ())
    return (not allow_unwrapped) or n <= 1

Prevention

When it happens

Trigger: RaisesGroup(ValueError, KeyError, allow_unwrapped=True); RaisesGroup((ValueError, KeyError), allow_unwrapped=True); or any RaisesGroup where other_exceptions is non-empty and allow_unwrapped is set.

Common situations: Adapting a RaisesGroup to also accept a lone exception (unwrapped) while still wanting to match a set of candidate types; migrating from raises(ValueError, KeyError) to group semantics.

Related errors


AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11). Data as JSON: /api/errors/26c59368930b4c34. Report an issue: GitHub.

Appendix: source

Thrown at src/_pytest/raises.py:971

            Callable[[BaseExceptionGroup[BaseExcT_1]], bool]
            | Callable[[ExceptionGroup[ExcT_1]], bool]
            | None
        ) = None,
    ):
        # The type hint on the `self` and `check` parameters uses different formats
        # that are *very* hard to reconcile while adhering to the overloads, so we cast
        # it to avoid an error when passing it to super().__init__
        check = cast(
            "Callable[[BaseExceptionGroup[ExcT_1|BaseExcT_1|BaseExceptionGroup[BaseExcT_2]]], bool]",
            check,
        )
        super().__init__(match=match, check=check)
        self.allow_unwrapped = allow_unwrapped
        self.flatten_subgroups: bool = flatten_subgroups
        self.is_baseexception = False

        if allow_unwrapped and other_exceptions:
            raise ValueError(
                "You cannot specify multiple exceptions with `allow_unwrapped=True.`"
                " If you want to match one of multiple possible exceptions you should"
                " use a `RaisesExc`."
                " E.g. `RaisesExc(check=lambda e: isinstance(e, (...)))`",
            )
        if allow_unwrapped and isinstance(expected_exception, RaisesGroup):
            raise ValueError(
                "`allow_unwrapped=True` has no effect when expecting a `RaisesGroup`."
                " You might want it in the expected `RaisesGroup`, or"
                " `flatten_subgroups=True` if you don't care about the structure.",
            )
        if allow_unwrapped and (match is not None or check is not None):
            raise ValueError(
                "`allow_unwrapped=True` bypasses the `match` and `check` parameters"
                " if the exception is unwrapped. If you intended to match/check the"
                " exception you should use a `RaisesExc` object. If you want to match/check"
                " the exceptiongroup when the exception *is* wrapped you need to"
                " do e.g. `if isinstance(exc.value, ExceptionGroup):"

View on GitHub (pinned to 0d6fbdeffa)