Skip to content

feat: Remove special cases for error messages - #5117

Closed
alexander-alderman-webb wants to merge 10 commits into
major/3.0from
webb/exception-message
Closed

feat: Remove special cases for error messages#5117
alexander-alderman-webb wants to merge 10 commits into
major/3.0from
webb/exception-message

Conversation

@alexander-alderman-webb

@alexander-alderman-webb alexander-alderman-webb commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Description

Always stringify exception values using the safe_str() method.

The special-casing of the message and detail attributes resulted in missing detail. The message attribute is a legacy from Python 2, and the detail attribute was returned directly because an old version of Starlette did not implement __str__ for some exceptions.

Issues

Closes #5050

Reminders

sentrivana and others added 8 commits October 24, 2025 08:59
### Description
Remove 3.6 from CI and CI templates on the new major branch.

#### Issues
Closes #4999

#### Reminders
- Please add tests to validate your changes, and lint your code using
`tox -e linters`.
- Add GH Issue ID _&_ Linear ID (if applicable)
- PR title should use [conventional
commit](https://develop.sentry.dev/engineering-practices/commit-messages/#type)
style (`feat:`, `fix:`, `ref:`, `meta:`)
- For external contributors:
[CONTRIBUTING.md](https://github.com/getsentry/sentry-python/blob/master/CONTRIBUTING.md),
[Sentry SDK development docs](https://develop.sentry.dev/sdk/), [Discord
community](https://discord.gg/Ww9hbqr)
Add `UnraisablehookIntegration` to the default integrations list.
@alexander-alderman-webb
alexander-alderman-webb requested a review from a team as a code owner November 19, 2025 08:22
@codecov

codecov Bot commented Nov 19, 2025

Copy link
Copy Markdown

❌ 14 Tests Failed:

Tests completed Failed Passed Skipped
24481 14 24467 1876
View the top 3 failed test(s) by shortest run time
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.001s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.002s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup_simple
Stack Traces | 0.002s run time
tests/test_exceptiongroup.py:186: in test_exceptiongroup_simple
    assert exception_values[1]["value"] == "simple"
E   AssertionError: assert 'simple (1 sub-exception)' == 'simple'
E     
E     - simple
E     + simple (1 sub-exception)
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.004s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.005s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]
tests.test_exceptiongroup::test_exceptiongroup
Stack Traces | 0.005s run time
tests/test_exceptiongroup.py:144: in test_exceptiongroup
    assert values == expected_values
E   AssertionError: assert [{'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 6, 'source': 'exceptions[2]', 'parent_id': 0}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 5, 'source': 'exceptions[1]', 'parent_id': 3}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 4, 'source': 'exceptions[0]', 'parent_id': 3}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 2, 'source': 'exceptions[0]', 'parent_id': 0}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 1, 'source': '__context__', 'parent_id': 0}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'type': 'test_suite', 'handled': False, 'exception_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'nested (3 sub-exceptions)'}] == [{'mechanism': {'exception_id': 6, 'handled': False, 'parent_id': 0, 'source': 'exceptions[2]', 'type': 'chained'}, 'type': 'TypeError', 'value': 'int'}, {'mechanism': {'exception_id': 5, 'handled': False, 'parent_id': 3, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ModuleNotFoundError', 'value': 'another_module'}, {'mechanism': {'exception_id': 4, 'handled': False, 'parent_id': 3, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ImportError', 'value': 'no_such_module'}, {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}, {'mechanism': {'exception_id': 2, 'handled': False, 'parent_id': 0, 'source': 'exceptions[0]', 'type': 'chained'}, 'type': 'ValueError', 'value': '654'}, {'mechanism': {'exception_id': 1, 'handled': False, 'parent_id': 0, 'source': '__context__', 'type': 'chained'}, 'type': 'RuntimeError', 'value': 'something'}, {'mechanism': {'exception_id': 0, 'handled': False, 'is_exception_group': True, 'type': 'test_suite'}, 'type': 'ExceptionGroup', 'value': 'nested'}]
E     
E     At index 3 diff: {'mechanism': {'type': 'chained', 'handled': False, 'exception_id': 3, 'source': 'exceptions[1]', 'parent_id': 0, 'is_exception_group': True}, 'type': 'ExceptionGroup', 'value': 'imports (2 sub-exceptions)'} != {'mechanism': {'exception_id': 3, 'handled': False, 'is_exception_group': True, 'parent_id': 0, 'source': 'exceptions[1]', 'type': 'chained'}, 'type': 'ExceptionGroup', 'value': 'imports'}
E     
E     Full diff:
E       [
E           {
E               'mechanism': {
E                   'exception_id': 6,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[2]',
E                   'type': 'chained',
E               },
E               'type': 'TypeError',
E               'value': 'int',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 5,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ModuleNotFoundError',
E               'value': 'another_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 4,
E                   'handled': False,
E                   'parent_id': 3,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ImportError',
E               'value': 'no_such_module',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 3,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'parent_id': 0,
E                   'source': 'exceptions[1]',
E                   'type': 'chained',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'imports',
E     +         'value': 'imports (2 sub-exceptions)',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 2,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': 'exceptions[0]',
E                   'type': 'chained',
E               },
E               'type': 'ValueError',
E               'value': '654',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 1,
E                   'handled': False,
E                   'parent_id': 0,
E                   'source': '__context__',
E                   'type': 'chained',
E               },
E               'type': 'RuntimeError',
E               'value': 'something',
E           },
E           {
E               'mechanism': {
E                   'exception_id': 0,
E                   'handled': False,
E                   'is_exception_group': True,
E                   'type': 'test_suite',
E               },
E               'type': 'ExceptionGroup',
E     -         'value': 'nested',
E     +         'value': 'nested (3 sub-exceptions)',
E           },
E       ]

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@alexander-alderman-webb
alexander-alderman-webb marked this pull request as draft November 19, 2025 08:32
@alexander-alderman-webb
alexander-alderman-webb marked this pull request as ready for review November 19, 2025 09:07

@sentrivana sentrivana left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In general looks good to me but we should make sure this is what we want because once we release it like this, it'll be tough to change.

How does this work with the old Starlette with the missing __str__? Or did we drop support for it already?

@alexander-alderman-webb

Copy link
Copy Markdown
Contributor Author

Good point regarding the old Starlette version.

Their HTTPException has a __str__ method since version 0.29 but we support version 0.16 and up.

So our fix to add special handling of a detail property fixed a Starlette and FastAPI specific problem. At the same time, users that happen to have detail properties on their exception lost information, as before #2193 __str__ was used to generate the Sentry exception value.

To keep both worlds happy we could use __repr__ instead of __str__ when

  • CustomException.__str__ is Exception.__str__; and
  • CustomException.__repr__ is not Exception.__repr__.

Starlette users would see a more meaningful value since __repr__ is added in Starlette 0.12.

Happy to hear other suggestions as well, especially since it'll be hard to change later.

@sentrivana

Copy link
Copy Markdown
Contributor

Hmm so if we detect someone explicitly redefined __repr__ but not __str__, we use __repr__? I'm unsure how often that'd trigger in situations where __str__ would still be more valuable.

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.

2 participants