Skip to content

Conversation

@alexander-alderman-webb
Copy link
Contributor

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

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

@alexander-alderman-webb alexander-alderman-webb requested a review from a team as a code owner November 19, 2025 08:22
@codecov
Copy link

codecov bot commented Nov 19, 2025

❌ 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
Copy link
Contributor

@sentrivana sentrivana left a comment

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
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.

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