Skip to content

Cannot pickle cyclic ExceptionGroup #137889

Open

Description

@dcolascione

Bug report

Bug description:

Pickling an exception group with a cycle, like so...

import pickle
inner = Exception("Inner")
outer = ExceptionGroup("outer", [inner])
outer.exceptions[0].foo = outer  
pickle.loads(pickle.dumps(outer))

...produces...

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
ValueError: second argument (exceptions) must be a non-empty sequence

Maybe BaseExceptionGroup needs a custom reducer that allows for two-phase initialization.

CPython versions tested on:

3.13

Operating systems tested on:

macOS

Activity

  1. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Aug 18, 2025
  2. serhiy-storchaka commented on Aug 18, 2025

    @serhiy-storchaka
    Member

    This is because BaseException.__reduce__() returns BaseException.args which is a tuple of the original arguments. And one of them is a mutable list.

    You can have issue with non-recursive ExceptionGroup:

    >>> excs = [ValueError()]
    >>> eg = ExceptionGroup('group', excs)
    >>> eg.args
    ('group', [ValueError()])
    >>> excs.clear()
    >>> eg.args
    ('group', [])
    >>> eg
    ExceptionGroup('group', [])
    >>> eg.exceptions
    (ValueError(),)
    >>> import pickle
    >>> pickle.loads(pickle.dumps(eg))
    ValueError: second argument (exceptions) must be a non-empty sequence

    I think there may be similar issues with other exceptions too.

    The BaseException exception constructor is split on two parts, __new__() and __init__(), and args is set in both of them.

  3. serhiy-storchaka commented on Aug 18, 2025

    @serhiy-storchaka
    Member

    We can change the args attribute so the second item is a tuple, but this will not work in general case, for ExceptionGroup subclasses. For example:

    class MyExceptionGroup(ExceptionGroup):
        def __new__(cls, code, msg, exceptions, **kwargs):
            self = ExceptionGroup.__new__(cls, msg, exceptions, **kwargs)
            self.code = code
            return self

    The args attribute of the MyExceptionGroup instance will have three items -- code, msg, and exceptions. Now exceptions is the third item, not the second item, but ExceptionGroup.__init__() cannot know this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions