Skip to content

compute error formatting state when python is initialized - #1936

Merged
t-kalinowski merged 4 commits into
mainfrom
bugfix/reentrant-error-hint
Oct 16, 2026
Merged

t-kalinowski merged 4 commits into
mainfrom
bugfix/reentrant-error-hint

Conversation

@kevinushey

@kevinushey kevinushey commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

conditionMessage_from_py_exception() set up two function-local statics on the first Python error of a session, and both of their initializers could run R code:

  • the error hint, computed by .py_last_error_hint();
  • traceback.format_exception_only, whose first import of traceback runs reticulate's import hook, which calls py_module_onload() in R.

R code can process events. If an event handler formats another Python error in that window, the nested call waits on the static's own init guard: libstdc++ deadlocks and libc++ aborts. RStudio Server hit the deadlock while answering an Environment pane request as the Python REPL started; see #1935 for the backtrace.

Both are now set when Python is initialized. R computes the hint before Python starts and passes it to py_initialize(). py_initialize() looks up traceback.format_exception_only before anything that can raise a Python error, and before reticulate's import hook is installed. Formatting an error no longer computes either one.

Testing

Two new tests in test-python-exceptions.R, each run in a fresh callr session:

  • One replaces .py_last_error_hint() with a function that errors, after Python has started, then formats a Python error. On reticulate 1.47.0 the message is the replacement's error; with this branch it's the Python error plus the hint.
  • One registers a py_register_load_hook("traceback", ...) hook that formats another Python error, then raises a first error. On macOS with 1.47.0 the child aborts with __cxa_guard_acquire detected recursive initialization; with this branch it passes.

The rest of test-python-exceptions.R passes. In the full suite the only failures were snapshot tests in test-py_require.R, which fail locally because of my uv version and default Python (3.13 vs 3.12).

Fixes #1935.

The hint is computed by R code, which can process events and re-enter
conditionMessage_from_py_exception(); a re-entrant call deadlocked
(libstdc++) or aborted (libc++) on the static's init guard.

Fixes #1935.
Compute the error hint in R and look up traceback.format_exception_only()
during initialization, instead of in function-local static initializers on
the first error. Both initializers could run R code (the hint function, or
the import hook's callback for 'traceback'), and a Python error formatted
from there re-entered the static's init guard.
@kevinushey kevinushey changed the title compute the error hint without a static initializer compute error formatting state when python is initialized Oct 15, 2026
Cache the error hint when the namespace loads and read it from C++ during
initialization. Keep the existing eight-argument R and native interfaces
so callers that initialize Python directly continue to work.
@t-kalinowski
t-kalinowski merged commit f1317d7 into main Oct 16, 2026
14 checks passed
@t-kalinowski

Copy link
Copy Markdown
Member

Thank you!

@t-kalinowski
t-kalinowski deleted the bugfix/reentrant-error-hint branch October 16, 2026 12:42
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.

Error hint static deadlocks (Linux) or aborts (macOS) when Python error formatting re-enters

2 participants