In earlier versions of the PEP ExceptionGroup was not immutable and split actually removed matching exceptions from it. 
It was also iterable so you could get a flat list of all the contained leaf exceptions. Then we changed it.

ExceptionGroup is a tree of exceptions, and the internal nodes of the tree (which are ExceptionGroups) have metadata on them -  context, cause, traceback.
If you want the full traceback of a leaf exception you need to concatenate the tracebacks of all the exceptions on the path from the root to the leaf.
If you flatten an ExceptionGroup and create a new list ExceptionGroup(list(eg)) you don't lose metadata (unless eg's tree has depth 1).
Similarly, if you add an exception it needs to have matching metadata to the ExceptionGroup you are adding it to. It's too easy to get that wrong.

split() and subgroup() take care to preserve the correct metadata on all the internal nodes, and if you just use them you only make safe operations.
This is why I am hesitating to add iteration utilities to the API. Like we did, people will naturally try that first, and it's not the safest API.

We actually have the  OSErrors example in the PEP, just above

except *OSerror as errors:
    raise errors.subgroup(lambda e: e.errno != errno.EPIPE) from None

On Sun, Feb 28, 2021 at 6:30 AM Guido van Rossum <> wrote:
We really don’t want users pushing non-exceptions into the list, nor do we want e.g. KeyboardInterrupt to be added to a (non-Base-) ExceptionGroup.

There’s a pattern for what you propose using the split() method and a lambda, or you can keep the exceptions in a list and re-wrap them at the end.

On Sat, Feb 27, 2021 at 20:41 Cameron Simpson <> wrote:
On 26Feb2021 02:44, Irit Katriel <> wrote:
>On Fri, Feb 26, 2021 at 2:00 AM Guido van Rossum <> wrote:
>> OT: Is ExceptionGroup *really* immutable in the current
>> implementation? As
>> long as the 'errors' field is a list, I think one could mutate the list
>> directly.
>It's not, but we were going to make it an immutable tuple.

Could you say why? Other than wanting to discourage mutation happy code
getting out there?

The reason I ask is that the scenario which comes to my mind is
something like:

    except *OSError as e:

AIUI "e" is an ExceptionGroup containing only OSErrors. So, one common
thing in my own code is this:

        do something with a file
    except OSError as e:
        if e.errno == ENOENT:
            # file is missing, but that is ok
            # because we treat it like an empty file
        elif ... some other ok situation ...

My natural inclination with an ExceptionGroup would be to winnow the
OSErrors I'm handed, and push the _unhandled_ errors back into the
original ExceptionGroup. That way, after the various except* clauses, a
nonempty ExceptionGroup would remain with the unhandled errors, and it
might perhaps be reraised then.

Cameron Simpson <>
--Guido (mobile)