Skip to content

RecursionError has empty or trailing-space message instead of 'maximum recursion depth exceeded' #8382

Description

@youknowone

Summary

The RecursionError raised when the recursion guard trips does not carry CPython's message 'maximum recursion depth exceeded'. Depending on which of RustPython's two recursion guards fires first, str(e) is either an empty string or the message with a stray trailing space.

Reproduction

# native C-stack guard (what trips by default, e.g. debug builds)
def f(): f()
try:
    f()
except RecursionError as e:
    print(repr(str(e)))
# RustPython: ''
# CPython:    'maximum recursion depth exceeded'
# logical depth-limit guard (force it with a low limit)
import sys
sys.setrecursionlimit(60)
def f(): f()
try:
    f()
except RecursionError as e:
    print(repr(str(e)))
# RustPython: 'maximum recursion depth exceeded '   <- trailing space
# CPython:    'maximum recursion depth exceeded'

CPython always yields exactly 'maximum recursion depth exceeded' (and e.args == ('maximum recursion depth exceeded',)). RustPython yields ('',) or ('maximum recursion depth exceeded ',).

Root cause

crates/vm/src/vm/mod.rs has two guards that construct the RecursionError message inconsistently, and neither matches CPython:

  • Logical depth guard — check_recursive_call (mod.rs:1877):

    Err(self.new_recursion_error(format!("maximum recursion depth exceeded {_where}")))

    With the common _where == "" this produces a trailing space: "maximum recursion depth exceeded ".

  • Native C-stack guard — with_recursion (mod.rs:1692) and resume_gen_frame (mod.rs:1767):

    return Err(self.new_recursion_error(_where.to_string()));   // 1692, _where == "" for normal calls
    return Err(self.new_recursion_error(String::new()));        // 1767

    Here _where is used as the whole message, so a normal call (empty _where) yields an empty message.

The two functions disagree on whether _where is a suffix appended to "maximum recursion depth exceeded" or the entire message. The fix is to make both paths produce "maximum recursion depth exceeded" (optionally + " while {context}" when context is present, with no trailing space when it is absent).

Reference

Verified against CPython 3.14.5.


Investigated and drafted by Claude; reviewed before filing.

Metadata

Metadata

Assignees

Labels

C-compatA discrepancy between RustPython and CPython

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions