[Behavioral] embedded-ripgrep V8-heap unbounded on file-listing (GH#54394 second spawn route)

Resolved 💬 3 comments Opened Jun 4, 2026 by adtendo Closed Jul 11, 2026

Behavioral Observation: embedded-ripgrep V8-heap unbounded on file-listing (GH#54394 second spawn route)

Category: Behavioral Pattern (not a bug or feature request)
Model: Claude Opus 4.6 (1M context) via Claude Code 2.1.162 (Claude Code)
Reproducibility: likely_systematic
Severity: high

Observed Behavior

On WSL2, the bundled ripgrep wrapper inside the claude binary consumes 5-6 GB RSS for a trivial file-listing Glob (rg --files --glob '**/X' --sort=modified), vs ~6.5 MB for the same query with external /usr/bin/rg 14.1.0 — a ~1000x delta that is pure V8/ugrep wrapper heap, not the ripgrep algorithm (measured: --sort=modified is linear/bounded). This is the same wrapper pathology as GH#54394 but surfaced via a SECOND, un-mitigated spawn route: the claude_agent_sdk bundled binary (claude_agent_sdk/_bundled/claude) is spawned as a Python subprocess, NOT via a login shell, so the documented USE_BUILTIN_RIPGREP=0 / CLAUDE_CODE_ENTRYPOINT=local-agent mitigation in ~/.bashrc is never inherited. Three concurrent embedded-rg from one SDK agent's parallel tool calls summed to ~15-18 GB and OOM-killed all WSL2 terminals on a 22 GB-capped host (2026-06-04 17:05). Reproduced: external rg on the identical storm query = 6.5 MB; only the embedded wrapper reaches GB-scale.

Expected Behavior

A single ripgrep search (file-listing OR content) should have a bounded per-process memory ceiling; the embedded wrapper should not exceed a few hundred MB. The agent-SDK subprocess should either honor the same ripgrep toggle as the CLI or default to a memory-safe rg path.

Claude's Self-Analysis

Root-caused via multi-agent forensic workflow: 4 evidence agents (process-tree from RAM-watchdog snapshots, workload-id from session jsonl, external-rg reproduction with /usr/bin/time -v, upstream research) + adversarial verification. One hypothesis (--sort=modified as an independent memory bug) was refuted by measurement.

User Impact

Full loss of all WSL2 terminals + running sessions (OOM cascade). Recurs whenever an agent-SDK workload issues parallel Globs/Greps on a large tree, because the shell-level #54394 mitigation does not reach the SDK-spawned bundled binary.

Local Mitigation

Local: (1) pass env={'USE_BUILTIN_RIPGREP':'0','CLAUDE_CODE_ENTRYPOINT':'local-agent'} via ClaudeAgentOptions (merges over os.environ in transport) for all SDK routes; (2) global RAM-watchdog with per-process rg RSS-ceiling SIGKILL at 3 GB, cmd-pattern targeting (embedded rg reports Name=claude).

Suggestion for Model Improvement

Add a hard per-process RSS/heap cap to the embedded ripgrep wrapper (the real fix, covers both file-listing and content-grep OOM); OR make the bundled SDK binary honor USE_BUILTIN_RIPGREP / ship a default external-rg path; AND document that SDK subprocesses do not inherit shell env so the #54394 mitigation must be passed via ClaudeAgentOptions.env. Reference GH#54394.

---
This observation was documented during a Claude Code session and reviewed by the user before submission.
No claims in this issue are estimated — all details are from actual session transcripts.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗