GeezerVison is showing github.com — traceback._compute_suggestion_error crashes on `__getattr__` raising · Issue #129605 · python/cpython
traceback._compute_suggestion_error crashes on __getattr__ raising #129605DescriptionBug report
Bug description:
This is a regression introduced in 3.13 because the new pyrepl. 3.14 is also affected.
Type this in an interactive 3.13/3.14 session:
>>> class a:
... def __getattr__(self, x):
... print(x)
... print(qq)
...
>>> b=a()
>>> b.pepe
pepe
qq
qq
Traceback (most recent call last):
File "<python-input-5>", line 1, in <module>
File "<python-input-3>", line 4, in __getattr__
NameError: name 'qq' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/usr/local/lib/python3.13/_pyrepl/__main__.py", line 6, in <module>
__pyrepl_interactive_console()
File "/usr/local/lib/python3.13/_pyrepl/main.py", line 59, in interactive_console
run_multiline_interactive_console(console)
File "/usr/local/lib/python3.13/_pyrepl/simple_interact.py", line 160, in run_multiline_interactive_console
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
File "/usr/local/lib/python3.13/code.py", line 314, in push
more = self.runsource(source, filename, symbol=_symbol)
File "/usr/local/lib/python3.13/_pyrepl/console.py", line 211, in runsource
self.runcode(code)
File "/usr/local/lib/python3.13/code.py", line 96, in runcode
self.showtraceback()
File "/usr/local/lib/python3.13/code.py", line 129, in showtraceback
self._showtraceback(typ, value, tb.tb_next, '')
File "/usr/local/lib/python3.13/code.py", line 145, in _showtraceback
self._excepthook(typ, value, tb)
File "/usr/local/lib/python3.13/_pyrepl/console.py", line 169, in _excepthook
lines = traceback.format_exception(
File "/usr/local/lib/python3.13/traceback.py", line 154, in format_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
File "/usr/local/lib/python3.13/traceback.py", line 1090, in __init__
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
File "/usr/local/lib/python3.13/traceback.py", line 1516, in _compute_suggestion_error
if hasattr(self, wrong_name):
File "<python-input-3>", line 4, in __getattr__
NameError: name 'qq' is not defined
And the interactive python session is terminated.
Under previous Python releases, with no pyrepl, the right exception is raised and the repl keeps going:
Python 3.9.21 (main, Dec 9 2024, 19:43:35)
[GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class a:
... def __getattr__(self, x):
... print(x)
... print(qq)
...
>>> b=a()
>>> b.pepe
pepe
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getattr__
NameError: name 'qq' is not defined
>>>
Notice that this problem raises SPECIFICALLY if __getattr__ raises a NameError exception. Other exceptions, like division by zero, don't break pyrepl.
CPython versions tested on:
3.13, 3.12, 3.11, 3.10, 3.9, 3.14
Operating systems tested on:
Linux
Linked PRs
Reacted by Gregory P. Smith and Konstantin Lopuhin Activityskirpichev commented on Feb 3, 2025 A quick fix (I'm not sure it's a right way):
diff --git a/Lib/traceback.py b/Lib/traceback.py
index 31c73efcef..abcc099c29 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -1520,8 +1520,11 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
# has the wrong name as attribute
if 'self' in frame.f_locals:
self = frame.f_locals['self']
- if hasattr(self, wrong_name):
- return f"self.{wrong_name}"
+ try:
+ if hasattr(self, wrong_name):
+ return f"self.{wrong_name}"
+ except NameError:
+ pass
try:
import _suggestions
>>> b.pepe
pepe
qq
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
b.pepe
File "<python-input-0>", line 4, in __getattr__
print(qq)
^^
NameError: name 'qq' is not definedjcea commented on Feb 3, 2025 Cool, but "qq" should not be searched in the class. Notice that __getattr__ is called twice, first for "pepe" and another one, incorrectly, for "qq". skirpichev commented on Feb 3, 2025 That might be helpful for suggestions, see 99e2e60. lopuhin commented on Feb 25, 2025 Another instance of the same error:
import traceback
class C:
@property
def foo(self):
return foo
try:
C().foo
except Exception:
print(traceback.format_exc())
gives (at least on Python 3.12):
% python t.py
Traceback (most recent call last):
File "/Users/konstantin/t.py", line 10, in <module>
C().foo
File "/Users/konstantin/t.py", line 7, in foo
return foo
^^^
NameError: name 'foo' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/konstantin/t.py", line 12, in <module>
print(traceback.format_exc())
^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.6/Frameworks/Python.framework/Versions/3.12/lib/python3.12/traceback.py", line 184, in format_exc
return "".join(format_exception(sys.exception(), limit=limit, chain=chain))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.6/Frameworks/Python.framework/Versions/3.12/lib/python3.12/traceback.py", line 139, in format_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.6/Frameworks/Python.framework/Versions/3.12/lib/python3.12/traceback.py", line 767, in __init__
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/homebrew/Cellar/[email protected]/3.12.6/Frameworks/Python.framework/Versions/3.12/lib/python3.12/traceback.py", line 1100, in _compute_suggestion_error
if hasattr(self, wrong_name):
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/konstantin/t.py", line 7, in foo
return foo
^^^
NameError: name 'foo' is not defined
so the property gets re-executed. In other cases this can also lead to an infinite chain of exceptions. changed the title [-]Crashing "pyrepl" with "__getattr__"[/-] [+]traceback._compute_suggestion_error crashes on `__getattr__` raising[/+] on Mar 20, 2025kak-0 commented on Apr 14, 2025 I encountered this bug when trying to log such an exception using logging.
The following code demonstrates the issue (using ValueError for demonstration purposes, even though the real issue I had was a NameError due to forgetting to write self.).
import logging
class Foo:
@property
def x(self):
raise ValueError("x")
def y(self):
return x
logger = logging.getLogger(__name__)
try:
Foo().y()
except Exception as e:
logger.exception("y failed") # raises!
print("done") # not reached!
logger.exception prints "--- Logging error ---" and then calls traceback.print_exception which fails to print the traceback. The exception raised by hasattr is propagated by logger.exception, preventing the program from continuing!
I think this should be considered a bug in logging: logger.exception should probably silently drop any exceptions raised while trying to handle logging errors instead of propagating the exceptions. Reacted by Konstantin Lopuhin duaneg commented on Jun 13, 2025 This cannot be reproduced in v3.14: bisection confirms it was fixed in 641253c, for #132385. We can probably close this. skirpichev commented on Jun 13, 2025 Looks valid in the master:
Python 3.15.0a0 (heads/main-dirty:2b0c684e07, Jun 13 2025, 06:46:26) [Clang 14.0.6 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class a:
... def __getattr__(self, x):
... print(x)
... print(qq)
...
... b=a()
...
>>> b.pepe
pepe
qq
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
b.pepe
File "<python-input-0>", line 4, in __getattr__
print(qq)
^^
NameError: name 'qq' is not defined
>>> jcea commented on Nov 11, 2025 Current Python 3.13.8 and 3.14.0 are not crashing anymore. I close this issue. shenker commented on May 30, 2026 I found another way to reproduce this bug that still works in 3.14.4. PR #129632's approach of wrapping the NameError branch in try/except Exception: return None would fix this.
Here's the real-world reproducer (and how I came across this bug). Create a marimo (0.23.8) notebook with the following cell (that imports statsmodel 0.14.6, which in turn imports patsy 1.0.2).
import polars as pl
import statsmodels.formula.api as smf
df = pl.DataFrame({"a2": range(10), "b": range(10, 20)})
model = smf.ols(
"a ~ b",
data=df.to_pandas(),
)
The CPython process running the marimo kernel will crash when the cell is run. A misspelled column name in a formula string triggers a NameError inside patsy's eval() call (where f_locals is a live VarLookupDict). Marimo calls traceback.print_exception directly from Python to format cell errors — unlike the REPL, which uses C-level PyErr_Display and is robust to this — so the KeyError(0) propagates uncaught and kills the notebook kernel.
This minimal reproducer mocks patsy's VarLookupDict, which implements __getitem__ but not __iter__, causing Python to fall back to the legacy sequence protocol (__getitem__(0), __getitem__(1), ...) and immediately raise KeyError(0):
import traceback
class LegacyMapping:
"""Minimal repro of patsy.eval.VarLookupDict — implements __getitem__ but not __iter__."""
def __getitem__(self, key):
if key == "x":
return 1
raise KeyError(key) # raises KeyError for integer keys too
try:
eval("nonexistent_var", {}, LegacyMapping())
except NameError as e:
traceback.print_exception(e)
# Expected: prints NameError cleanly
# Actual (3.13+): KeyError: 0 propagates out of print_exceptionadded 3 commits that reference this issue on Oct 20, 2026gh-129605: Add more exception handling when computing suggestions in …
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|