Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: Remove special cases for error messages #5117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
alexander-alderman-webb wants to merge 3 commits into major/3.0
base: major/3.0
Choose a base branch
Loading
from webb/exception-message

Conversation

@alexander-alderman-webb
Copy link
Contributor

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

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

sentry[bot] reacted with hooray emoji
Copy link

codecov bot commented Nov 19, 2025
edited
Loading

❌ 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 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?

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.

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

Reviewers

@sentrivana sentrivana sentrivana left review comments

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /