Skip to content

py_lsp: py_eval_expr_type infinite recursion — Python missed in 9f981a1 depth-guard fix #720

Description

@Rakesh-207

Bug

py_eval_expr_type in py_lsp.c enters unbounded recursion on certain Python expression patterns, hanging the indexer at 100% CPU indefinitely. The process never terminates and must be killed manually.

Root Cause

Commit 9f981a1 ("Bound LSP resolve recursion depth") added depth guards to three LSP resolvers to prevent exactly this class of bug:

Language File Guard
Java java_lsp.c walk_depth cap 512
C/C++ c_lsp.c walk_depth cap 512
TypeScript ts_lsp.c member_depth cap 64
Python py_lsp.c — none —

Python was missed. The commit message itself notes: "The expression-eval guards (eval_depth) did not cover these walks."

Stack Trace (from sample)

The worker thread spins in a self-referential call chain with no depth cap:

extract_worker
  → cbm_extract_file
    → cbm_run_py_lsp
      → py_lsp_process_file
        → py_resolve_calls_in
          → py_process_statement
            → py_eval_expr_type          ← recurses here
              → py_eval_expr_type
                → py_eval_expr_type
                  → py_eval_expr_type     ← 10+ levels, never terminates

The main thread is blocked on _pthread_join waiting for this worker. Memory grows to ~8 GB from the deep recursion stack. No log output after the file starts extracting — it just burns CPU forever.

Trigger

A single ~6,000-line Python file with complex runtime code (PyTorch tensors, MediaPipe, GStreamer WebRTC, Modal decorators, deeply chained method calls). The file is valid, production Python. I'm not able to share the source, but the pattern that triggers it involves deeply nested method-call chains on dynamically-typed objects where the type resolver can't find a base case.

Reproduction

  1. Index a repo containing a large Python file (~6K lines) with complex expression nesting
  2. The parallel extraction phase reaches the file and hangs
  3. sample <pid> shows the recursive py_eval_expr_type chain above
  4. Process must be killed manually

Environment

  • Version: 0.8.1 (latest, darwin/arm64)
  • OS: macOS 26.1 (Apple Silicon)
  • Memory budget: 4 GB (default), 8 GB RAM system
  • Workers: 8

Suggested Fix

Add an eval_depth cap to py_eval_expr_type — same pattern as the walk_depth cap 512 added for Java/C in 9f981a1. Past the cap, the subtree resolves as unknown (graceful degradation). This is consistent with how the other resolvers were fixed.

A regression test (similar to test_stack_overflow.c) with a synthetic deeply-nested Python expression fixture would prevent regressions.

Workaround

Excluding the file via .cbmignore allows the rest of the repo (316 other files) to index cleanly with 6,067 nodes and 21,651 edges.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.stability/performanceServer crashes, OOM, hangs, high CPU/memory

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions