Skip to content

Comments

code: use current excinfo when formatting chained BaseExceptionGroup#14221

Open
bysiber wants to merge 2 commits intopytest-dev:mainfrom
bysiber:fix-excinfo-exceptiongroup-chain
Open

code: use current excinfo when formatting chained BaseExceptionGroup#14221
bysiber wants to merge 2 commits intopytest-dev:mainfrom
bysiber:fix-excinfo-exceptiongroup-chain

Conversation

@bysiber
Copy link

@bysiber bysiber commented Feb 20, 2026

In repr_excinfo, when the current exception e is a BaseExceptionGroup, the code uses the original excinfo parameter instead of excinfo_ (which tracks the current exception as the method walks the __cause__/__context__ chain):

while e is not None and id(e) not in seen:
    seen.add(id(e))

    if excinfo_:
        ...
        if isinstance(e, BaseExceptionGroup):
            traceback = filter_excinfo_traceback(self.tbfilter, excinfo)   # should be excinfo_
            reprtraceback = ReprTracebackNative(
                format_exception(
                    type(excinfo.value),   # should be excinfo_
                    excinfo.value,         # should be excinfo_
                    ...

On the first loop iteration this doesn't matter since excinfo_ starts as excinfo. But when walking the exception chain, excinfo_ is reassigned to ExceptionInfo.from_exception(e) while excinfo still points to the original. So for any chained BaseExceptionGroup, the traceback filtering is applied to the wrong exception and format_exception formats the original exception instead of the current one.

The non-group branch already correctly uses excinfo_:

else:
    reprtraceback = self.repr_traceback(excinfo_)

This fix replaces the three occurrences of excinfo with excinfo_ inside the BaseExceptionGroup branch.

In repr_excinfo, the BaseExceptionGroup branch incorrectly uses
the original excinfo instead of excinfo_ (which tracks the current
exception in the chain). This means that when walking __cause__ or
__context__ chains, filter_excinfo_traceback and format_exception
always operate on the first exception rather than the current one.

The non-group branch on the else side already uses excinfo_
correctly.
@The-Compiler
Copy link
Member

Similarly to your other PRs I commented on, could you please add a regression test to catch this issue?

Test that when a BaseExceptionGroup appears as a chained exception,
the traceback output correctly shows both the ExceptionGroup and the
outer exception, using the current excinfo_ rather than the original.
@bysiber
Copy link
Author

bysiber commented Feb 20, 2026

Added a regression test — it creates a chained exception where a BaseExceptionGroup is the context of an outer ValueError, and verifies the output correctly shows both exceptions with the right traceback chain.

if isinstance(e, BaseExceptionGroup):
# don't filter any sub-exceptions since they shouldn't have any internal frames
traceback = filter_excinfo_traceback(self.tbfilter, excinfo)
traceback = filter_excinfo_traceback(self.tbfilter, excinfo_)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To improve clarity, let's rename excinfo_ to current_excinfo.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants