close
Skip to content

Fix __notes__ leaking across chained exceptions (#3960)#4088

Open
kanchanthapa wants to merge 1 commit intoTextualize:masterfrom
kanchanthapa:fix/3960-traceback-notes-chain
Open

Fix __notes__ leaking across chained exceptions (#3960)#4088
kanchanthapa wants to merge 1 commit intoTextualize:masterfrom
kanchanthapa:fix/3960-traceback-notes-chain

Conversation

@kanchanthapa
Copy link
Copy Markdown

When rendering a chained exception, Rich previously read notes once from the outermost exception and reused the same list reference on every Stack built inside the while-loop. This caused notes added to one exception to appear on every exception in the chain, which contradicts CPython's native traceback module behavior (notes are only rendered on the specific exception they were added to via PEP 678's add_note()).

Move the notes assignment inside the extraction loop so each exception contributes its own notes. Covers cause, context, and ExceptionGroup chains.

Adds test_notes_isolated_per_exception_in_chain covering both cause and context chaining.

Type of changes

  • [x ] Bug fix
  • New feature
  • Documentation / docstrings
  • [ x] Tests
  • Other

AI?

  • [x ] AI was used to generate this PR

AI generated PRs may be accepted, but only if @willmcgugan has responded on an issue or discussion.

Checklist

  • I've run the latest black with default args on new code.
  • I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate (see note about typos above).
  • I've added tests for new code.
  • I accept that @willmcgugan may be pedantic in the code review.

Description

Please describe your changes here.

Fixes #3960.

When displaying a chained exception whose __notes__ were only added to
the outermost exception, Rich rendered those notes under every exception
in the chain instead of only on the one they were added to. This
contradicts CPython's built-in traceback behavior.

Root cause

Traceback.extract() reads exc_value.__notes__ into a local variable
once, before the while True loop that walks __cause__ / __context__.
Every Stack built inside the loop then shares the same list reference,
so every stack ends up with the outermost exception's notes.

Fix

Move the notes = getattr(exc_value, "__notes__", None) or [] assignment
inside the loop so each iteration reads from the current exc_value.

Test

Added test_notes_isolated_per_exception_in_chain covering both
__cause__ (explicit raise ... from ...) and __context__ (implicit
chaining) cases.

QA

  • pytest — 953 passed, 25 skipped
  • mypy -p rich — no issues
  • black --check rich/traceback.py tests/test_traceback.py — unchanged
  • Coverage on rich/traceback.py unchanged at 88%

AI disclosure

Per AI_POLICY.md: drafted with the assistance of Claude (Anthropic). The
fix approach matches the root-cause analysis posted in issue #3960 by the
reporter; I have reviewed every line and run the full QA pipeline locally.

Before / after

Before (bug):

Important: Link to an issue or discussion regarding these changes.

When rendering a chained exception, Rich previously read __notes__ once
from the outermost exception and reused the same list reference on every
Stack built inside the while-loop. This caused notes added to one
exception to appear on every exception in the chain, which contradicts
CPython's native traceback module behavior (notes are only rendered on
the specific exception they were added to via PEP 678's add_note()).

Move the __notes__ assignment inside the extraction loop so each
exception contributes its own notes. Covers __cause__, __context__, and
ExceptionGroup chains.

Adds test_notes_isolated_per_exception_in_chain covering both __cause__
and __context__ chaining.
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.

[BUG] Traceback __notes__ from outermost exception shown on all exceptions in chain

1 participant