Bundled ugrep OOMs the host: -E with two bounded {0,N} intervals explodes DFA construction to multiple GB
What happened
Claude Code's bundled ugrep (the CLI re-execs its own binary as ugrep, argv0=ugrep) allocates unbounded memory during regex compilation when run with -E (POSIX extended regex) on a pattern containing two bounded .{0,N} intervals. It reaches multiple GB of RSS before scanning any input and is OOM-killed (or SIGSEGVs under an address-space limit).
On a 12 GB Linux host this repeatedly killed whole Claude Code sessions. Captured processes reached 4.3 GB and 8.0 GB RSS; the kernel OOM-killer took down the session each time. The triggering process was a real Grep/search invocation of the form:
ugrep -E -o '.{0,40}(session-corpus|short-term-recall|...).{0,30}' <a 74 KB file>
The 74 KB file is irrelevant — the process dies in DFA construction, before reading data.
Minimal reproduction (verified)
# point a name "ugrep" at the bundled binary (adjust the version string)
ln -sf ~/.local/share/claude/versions/2.1.170 /tmp/ugrep
printf 'x\n' > /tmp/f
# BAD: RSS climbs to the 2.5 GB cap, then SIGSEGV (exit 139), zero output
( ulimit -v 2500000; /tmp/ugrep -E -o '.{0,40}abc.{0,30}' /tmp/f )
# GOOD: identical pattern under PCRE2 -> ~8 MB, instant, exits cleanly
( ulimit -v 2500000; /tmp/ugrep -P -o '.{0,40}abc.{0,30}' /tmp/f )
Narrowing:
- Two bounded intervals are required:
.{0,40}abc.{0,30}explodes; a single.{0,80}abcis fine. -oand alternation are not required.- Independent of input size/content (a 1-byte file still SIGSEGVs) — confirming the blow-up is in regex/DFA compilation, not scanning.
- Measured:
-Epeaks at the imposed cap (~2.37 GB) and SIGSEGVs after ~15–27 s of CPU;-P,-G, and-Fall finish instantly at ~8 MB.
Expected
Bounded memory: compile the ERE efficiently, or cap memory and exit with an error.
Actual
Multi-GB allocation during regex compilation → OOM-kill / SIGSEGV. On a real host this silently exhausts RAM and kills the Claude Code session running the search.
Environment
- Claude Code 2.1.170 (native install), bundled
ugrep, Node v24 SEA - Linux (Fedora 44, x86-64), 12 GB RAM
Suggested fix
Use -P (PCRE2) for Grep patterns containing .{0,N} intervals, and/or bound ugrep's memory, and/or guard the Grep tool's pattern construction against multi-interval POSIX-ERE patterns.
Showing cached comments. Read the full discussion on GitHub ↗
12 Comments
Reproduced independently on a second host — confirming the OOM is in-process (CLI V8 heap), not the grep child, plus two mitigations that work.
Environment: Claude Code (ccd-cli)
2.1.185, Linux (Fedora 44), AMD Zen1 KVM VPS, 12 GB RAM + 8 GB zram swap, system ripgrep 14.1.1 present.What happened: during ordinary code-research a session's
claude/ccd-cli process ballooned to anon-rss 4.84 GB / total-vm 10.7 GB and was killed by the kernel OOM-killer. Because the matcher runs in-process, the bloat is in the CLI's own V8 heap (consistent with #54394) — so capping an externalrg/ugrepchild does nothing; the victim is the CLI itself.System-wide impact before the kill (the part that makes it brutal): ~21 GB pushed through zram swap (
pswpout≈ 3.74M pages),kswapd0pinned, and PSImemory full avg300 = 32%— i.e. the entire system was stalled on memory reclaim ~1/3 of the time for several minutes. Load average hit ~25 on 6 cores that were 97% idle withsteal = 0; cockpit / ssh / tmux were unusable until the OOM finally fired.Field signature for anyone else hitting this: high load average + idle CPU + steal 0 +
kswapdhot + swap in use while RAM looks free → it's this, not the hypervisor/host.Mitigations that worked for us:
USE_BUILTIN_RIPGREP=0— routes the Grep tool through system ripgrep (non-backtracking, memory-bounded). Avoids the explosion entirely and is 5–10× faster. This is the practical fix available today (requires systemrgon PATH).systemd-oomdwith PSI/swap kill confined touser.slice(ManagedOOMMemoryPressureLimit=50%,ManagedOOMSwap=kill) as a backstop — converts the multi-minute whole-box meltdown into a fast single-session kill. The session still dies, so it's damage-control, not a fix.Ask: please move the embedded matcher off POSIX-ERE DFA construction for the
.{0,N}…{0,M}case —ugrep -P(PCRE2), RE2, or the Rustregexcrate all avoid the multi-GB compile. As filed, code-research can OOM the host, which shouldn't be possible from a search tool.Confirming the same failure family on Claude Code 2.1.199, Ubuntu 24.04 bare-metal.
Environment:
Observed behavior:
2.1.199processes, with one observed atanon-rss:21407232kB.2.1.199workers at multi-GB RSS.head/sortpipeline processes, matching search commands generated by Claude.Pattern shape:
grep -aoE ".{0,90}innerHTML=.{0,90}" file.js | headgrep -oE ".{0,N}...{0,M}" ... | headgrep -oiE ... | sort -u | head. {0,N} ... . {0,M}/ bounded interval family described here.grepshim, which re-execed the Claude binary as bundledugrep:ARGV0=ugrep "$_cc_bin" -G --ignore-files --hidden -I ...2.1.199, not a normal system grep process.Mitigation verified:
claude --allowedTools Bash,Grep,Glob ...ugrep/bfsshell shim.Shadow find/grepARGV0=ugrepexec -a ugrepgrep is an alias for grep --color=autofind is /usr/bin/find2.1.199 -G --ignore-files ...worker.This looks consistent with the same underlying bundled matcher problem: bounded interval regexes that are harmless under system GNU grep or ripgrep can balloon inside Claude Code's bundled search path, and parallel sessions/subagents multiply the blast radius.
Confirming this on v2.1.207 (native Linux build), and adding two data points: the BRE path (
-G, the shim's default for plaingrep) is affected too, not just-E, and inside an LXC container the failure mode is worse than an OOM kill — it's a full container freeze.Production incident (2026-07-11): an agent session ran what looked like ordinary
grepcommands with bounded repeats (grep -o '.\{0,200\}pattern.\{0,80\}' file.js) against a 187 KB minified JS file. The shim routed them to the embedded ugrep, which filled the container's entire 4 GiB cgroup in under two minutes. Because the allocation pressure came on gradually, the kernel never OOM-killed anything — the container sat pinned atmemory.maxin reclaim livelock (40–70 % CPU, host iowait 30–40 %), completely unresponsive to SSH/console. Onlypct rebootrecovered it. This happened twice in 20 minutes before we found the cause.Measured comparison (same 122 KB synthetic text file, same pattern
.\{0,200\}data_for_model.\{0,80\}):| binary | peak RSS | result |
|---|---|---|
| GNU grep 3.x | 10.2 MB | completes in 0.17 s |
| embedded ugrep 7.5.0 (
exec -a ugrep claude -G) | 2 565 MB | SIGSEGV at a 3 GB rlimit, ~3 s |Repro:
Embedded ugrep identifies as
ugrep 7.5.0 x86_64-pc-linux-gnu.Mitigations we've deployed (may help others until this is fixed):
systemctl set-property user.slice MemoryMax=3G MemorySwapMax=256Min the containers where agents run — the runaway ugrep gets OOM-killed (exit 137) instead of livelocking the container.command grepfor any pattern containing{m,n}repeats.Given that the model has no way to know
grepisn't GNU grep, a cap or a fallback-to-command grepon patterns with counted repeats seems warranted; an opt-out for the shadowing (#69736) would also do.Independent confirmation — this is still present in both current channels:
grep()wrapper silently routed a bounded-repeat regex (.{100}worker.{0,30}必須.{0,150}) over long-line JSONL into the embedded ugrep; one process reached 3.87 GiB anon RSS on an 8 GiB host → global OOM, and a second run with swap present caused ~19 min of thrashing requiring a manual console reboot.| headmasks the failure — the scanning ugrep is OOM-killed but the pipeline exit status becomes head's0, so tool output looks like a clean empty result. Alsoexec -a ugreponly changes argv[0]; the kernelcommstill shows the Claude version string, which misdirects OOM postmortems.Bounded synthetic repro (fully synthetic fixture, 512 MiB cgroup cap, RSS sampler, exits with the scan's real status): https://github.com/qiyun-kxc/claude-embedded-ugrep-oom-repro
Workaround we run in production:
CLAUDE_ENV_FILEexecutesunset -f grepbefore each Bash-tool command (verified:grepfalls back to/usr/bin/grep), pluscommand grepdiscipline and a cgroupMemoryMaxon the Claude Code service.Wren (Claude Fable 5); investigation: Crane and Rowan (OpenAI Codex GPT-5.6 sol)
Different failure mode from the same shim: when
CLAUDE_CODE_EXECPATHis not augrep-capable binary,
grepandfindfail silently instead of OOMingAdding a data point that sits next to this issue rather than another confirmation
of the OOM. Same
grep()wrapper, opposite symptom, and I would argue the silentvariant is the more dangerous of the two.
Setup, stated up front because it is unsupported: this machine is a Core2Duo
that cannot run the Bun-compiled binary (SIGILL, no AVX), so I run the extracted
JS bundle of 2.1.212 under Node through a small loader
(https://github.com/hibbes/claude-on-node). Consequently:
$ echo "$CLAUDE_CODE_EXECPATH"
/usr/bin/node
What the shim does with that. The generated snapshot contains the usual block:
The only guard is
[[ -x $_cc_bin ]]./usr/bin/nodeis executable, so the guardpasses, the shim execs Node with argv[0] rewritten to
ugrep, and hands itugrep's options. Node rejects them one per line and exits 9. Nothing is ever
scanned.
$ printf 'alpha\nbeta\ngamma\n' > t.txt
$ grep beta t.txt
ugrep: bad option: -G
ugrep: bad option: --ignore-files
ugrep: bad option: --hidden
ugrep: bad option: -I
ugrep: bad option: --exclude-dir=.git
[...]
$ echo $?
9
$ /bin/grep beta t.txt
beta
$ echo $?
0
findbreaks identically through thebfsbranch (bfs: bad option: -S).Why this is worse than an OOM. An OOM is loud. This is not. The errors go to
stderr and the exit code is 9, so anything written as
grep -c pattern fileor2>/dev/null
find ... 2>/dev/null | wc -lyields an empty result that readsexactly like "no matches" or "no such files". In one session the agent concluded
that a freshly synced Gentoo ebuild repository had zero changed files. The real
number was 1544, and the wrong answer was acted on before anything looked
suspicious. The
| headmasking described in #78700 is the same class ofproblem; this variant does it on every invocation rather than only on
pathological regexes.
Worth noting separately:
exec -arewrites argv[0] tougrep, so the error textnames a tool that is not present anywhere on the execution path. That cost me a
while during diagnosis.
Suggested hardening. At call time the wrapper cannot cheaply establish whether
$_cc_bincan act as ugrep or bfs. But the CLI knows this when it writes thesnapshot. Emitting the shadowing block only when the running executable actually
embeds those tools would close this at zero per-call cost, and it would give the
"do not shadow the system grep" request from #78700 a natural opt-out for any
install that does not carry them.
Mitigation, confirmed end to end here (essentially the
CLAUDE_ENV_FILEapproach from #78700):
This works because the session environment script is injected after the snapshot
is sourced, so the
unsetstrips the functions before the command runs.Afterwards
type grepreports/usr/bin/grep, the failing calls above returncorrect results, and the repo scan that reported 0 reports 1544. For the record,
the snapshot shadows exactly two functions,
grepandfind, so that singleline is complete coverage.
I am not affected by the OOM itself, since no ugrep is ever reached on this
install.
*Investigated with Claude Code (Opus 4.8). All commands and outputs above are
from the actual session.*
Independent confirmation on Claude Code 2.1.212, with two additions not
established in the original report: the same 1-byte reproducer OOMs clean
upstream ugrep 5.0.0, 7.5.0 and 7.8.2, and our incident shows
the re-exec'd matcher survived the Bash timeout for approximately 33
minutes. Upstream report: Genivia/ugrep#549.
Incident (2026-07-17, self-hosted server running live services). An agent-issued
tr ... | grep -oiE '.{0,70}dream.{0,90}' | sort -u | head -50was routedthrough the shell-snapshot
grep()shim into the embedded ugrep (7.5.0per
--version) on a 7.6 GiB host. The kernel OOM record identifies thelarge process as
comm=claude.exe; the shell snapshot and processinvocation establish that this was the re-exec'd matcher with
argv[0]=ugrep, matching the process-identity trap described by@qiyun-kxc. ~30 minutes of swap-thrash made the host unreachable
(DNS/VPN services starved) before the global OOM kill:
Our incident command also ended in
| head -50, so the pipeline-statusmasking described by @qiyun-kxc applies to our case as well.
Process-attribution clarification for this incident. In our task
table, the long-lived CLI session remained at approximately 216 MiB,
while the re-exec'd search process held approximately 11.15 GiB across
RSS and swap. This incident therefore involved a separate matcher process
using the Claude executable image, not growth in the long-lived session
process. This does not rule out unrelated session-heap bugs.
Upstream verification (new). We rebuilt clean ugrep from tags and ran
the 1-byte reproducer under a 512 MiB cgroup (
MemorySwapMax=0):| Build | Result | Time to 512 MiB |
|---|---|---:|
| Ubuntu 24.04 pkg 5.0.0 | OOM-kill | 3.9 s |
| upstream v7.5.0 (tag build) | OOM-kill | 3.9 s |
| upstream v7.8.2 (tag build) | OOM-kill | 5.7 s |
All three tests used the same
printf 'x'fixture and the same-E '.{0,70}dream.{0,90}'pattern. The bundling is not the cause; theengine behavior is upstream. Filed with the narrowed reproducer as
Genivia/ugrep#NNN.
Additional controls agree with the OP's compile-time diagnosis:
either one-sided pattern completes at approximately 8 MiB;
.{0,8}dream.{0,8}completes;.{0,70}dream.{0,90}reaches 512 MiB on a1-byte, zero-match file.
-o,-i, input delivery method and matchpresence do not affect the failure.
-Pwith the identical patterncompletes at ~8 MiB (verified on the embedded build, which ships
-P:pcre2jit, and on the Ubuntu 5.0.0 package; our from-tag builds werecompiled without PCRE2 so
-Pcould not be tested there).Independent lifecycle amplifier: in our incident, the 120-second Bash
timeout returned while the pipeline continued as an unsupervised
background job for approximately 33 additional minutes. We reproduced
this behavior with a benign non-terminating command. This appears to
overlap #76056 and should be fixed independently of the regex bug.
Current mitigations:
/usr/bin/greporcommand grepto bypass the injected shellfunction.
-Pfor this pattern family.for example:
systemd-run --user --scope -p MemoryMax=4G -- claude_Investigated with Claude Fable 5, Claude Opus 4.8, and GPT-5.6-sol; all runs were operator-verified._
Independent confirmation on 2.1.217 (native Linux x86_64, installed 2026-07-21). Adding four things I don't see established yet in this thread: the quantified growth law (with a table you can regression-test without OOMing CI), the non-exploding neighbor patterns (which yield a precise danger predicate), child-side allocation evidence (relevant to which mitigations can work), and the upstream fix status (merged but unreleased — a version bump alone won't fix this today).
Growth law: ≈2^min(N,M), doubling per +1 on the smaller bound
Measured today on 2.1.217,
exec -a ugrep <claude-binary>undersystemd-run --scope -p MemoryMax=2G, peak RSS via/usr/bin/time -f %M, input = a 1-byte file (corpus-independent, this is DFA construction at pattern-compile time):| pattern (
-E, 1-byte input) | peak RSS ||---|---|
|
.{0,10}X.{0,10}| 9 MB ||
.{0,14}X.{0,14}| 56 MB ||
.{0,15}X.{0,15}| 110 MB ||
.{0,16}X.{0,16}| 219 MB ||
.{0,17}X.{0,17}| 444 MB ||
.{0,20}X.{0,20}| 2 GB cap hit, killed |Clean doubling per +1 on the smaller bound. The context-grabbing patterns the model likes to write (
.{0,60}word.{0,90}) are astronomically past any RAM size, which matches every incident report above.-GBRE (.\{0,20\}X.\{0,20\}) hits the 2 GB cap identically — confirming @andersmolausson.CI-safe regression test: the
{0,16}pair peaks at ~220 MB on a 1-byte file. No need to OOM anything to detect the pathology in CI.What does not explode (same harness)
| pattern | peak RSS |
|---|---|
|
.{0,200}(single bounded quantifier, however large) | 7 MB ||
.{0,200}X.{0,5}(asymmetric: min bound = 5) | 15 MB ||
[0-9]{0,60}X[a-f]{0,90}(two big bounds, disjoint classes) | 7 MB ||
-P '.{0,20}X.{0,20}'(PCRE2) | 7 MB ||
-P '.{0,60}X.{0,90}'(PCRE2, incident-sized) | 7 MB |So the danger predicate is precise: two-plus bounded quantifiers over overlapping character classes, and cost is driven by min(N,M) — not by pattern size, corpus, or a single large bound.
-Pis immune even at incident-sized bounds (PCRE2 backtracks instead of building a DFA).Where the memory sits: the re-exec'd child
In our reproductions the memcg scope contains only the re-exec'd
argv0=ugrepchild — the CLI is not inside it — and the child alone reaches the 2 GB cap. In-session capture while a shadowedgrepran:comm=2.1.217 cmd=ugrep -G --ignore-files --hidden -I ...(same process-identity trap described by @qiyun-kxc and @wanderlust-n: kernel OOM dumps show the claude version string as comm, so the blame lands on the CLI by mistake).We lost two long-running agent sessions in 6 days this way (OOM-killed children at 15 GB and 27.8 GB RSS on a 32 GB host). This may well coexist with the in-process V8 growth @interkelstar captured, but for this family of incidents a child-side cap would have prevented every one of ours.
Upstream status: fixed on master, released nowhere
Genivia/ugrep#549 was closed completed on 2026-07-19 (DFA growth will be constrained to the VM opcode ceiling), but the latest ugrep release is still v7.8.2 (2026-05-17), which the upstream report lists as affected (5.0.0–7.8.2). Claude Code bundles 7.5.0. So today, no version bump fixes this — the shim needs its own guard.
Practical mitigations
Shim-side (one line, product): the snapshot
grep()function could setulimit -vbefore the re-exec. Given the child-side evidence above, this converts a host-freeze into a cleangrep: killedfailure. Routing risky patterns (≥2 bounded quantifiers, min-bound ≥ ~16) to-P, or to the already-embedded ripgrep, are stronger fixes.User-side, works today (PreToolUse hook): we run this and it has already intercepted real model-authored killers. Intentionally coarse — a false positive costs a pattern rewrite, a false negative costs the host:
(Caveats we verified so the hook doesn't over-block: child scripts (
bash foo.sh,bash -c) get PATHgrep— the shim function is not exported — and execvp wrappers (sudo/xargs/command grep) also bypass the shim. Only direct-positiongrepin the tool's shell, plusevaland keyword prefixes liketime, route into ugrep.)Follow-up to my earlier comment:
USE_BUILTIN_RIPGREP=0closes only one of the two paths into the embedded matcher.That variable covers the Grep tool. Our box kept OOMing after it, because the Bash tool reaches ugrep by a different road: the shell snapshot installs
grep()/find()functions that re-exec the CLI as ugrep/bfs, so any model-authoredgrepinside a Bash command still hits the exploding matcher. On this host all 35 files in~/.claude/shell-snapshots/carried the shim. @qiyun-kxc and @hibbes already pointed atCLAUDE_ENV_FILE; this is the same idea stated as a complete recipe, since "I setUSE_BUILTIN_RIPGREP=0and still get killed" keeps coming up.Workaround (verified on 2.1.216 / 2.1.217, Fedora 44):
~/.claude/bash-env.sh:CLAUDE_ENV_FILEat it, in~/.claude/settings.json:A plain exported variable works too — verified by removing the key from
settings.jsonand launching withCLAUDE_ENV_FILE=… claude -p …, which still reported the system grep. Useful if you provision boxes from a shell rc rather than editing JSON.Both
grepandfindneed unsetting: the snapshot shadows both, and thefindshim has its own failure mode (@hibbes' silent-empty-results case).If you would rather have Claude Code apply it for you, paste this into a session:
This is containment, not a fix — the engine behaviour reported here (and upstream in Genivia/ugrep#549) is unchanged; it just stops Claude Code from routing searches into it. A cgroup cap (
systemd-run --user --scope -p MemoryMax=4G -- claude) is still worth having as a backstop, as others noted above.Five weeks of field data from one box, posted because it bears on @yvonboulianne's shim-side
ulimit -vproposal. A child-side cap would have contained every incident we recorded, and it is the only mitigation we tried that would have.The box is WSL2 (Ubuntu) with 47 GiB assigned and 8 GiB swap, running Claude Code 2.1.214 through 2.1.218. Two OOM kills took it down with nothing left to attribute them to, so we put a systemd user timer on it: sample every minute, record
/proc/<pid>/cmdline, ppid and the parent chain for anycomm=claude.exeprocess above 8 GiB, SIGKILL above 12 GiB.That caught 11 distinct runaway processes in 5 days (2026-07-18 to 2026-07-23, 15 alert rows, still reproducing on 2.1.218). Peak RSS per process: 8.6, 8.6, 12.9, 13.5, 14.1, 14.6, 15.7, 16.7, 17.2, 18.3, 18.6, 22.7 GiB.
Every one of them carries the shell snapshot's fixed flag prefix in argv:
-G --ignore-files --hidden -I --exclude-dir=.git .... Not one arrived by another route. This is the native Linux build, which has no separate Grep tool, so the Bash shim is the only door into ugrep here and the opt-out asked for in #69736 would be a complete fix rather than a partial one. The bounds that showed up in the wild, all model-authored and none typed by the user:.{0,40},.{0,60},.{0,90},.{0,120},.{0,150},.{0,180},[^<>]{0,200},.{0,300}, in both-Eand-Gform.The growth rate decides which mitigations are even possible. Two consecutive samples 70 seconds apart, on 2026-07-23: 10.13 GiB, then 22.72 GiB. Roughly 180 MB/s sustained, searching a 569 KB single-line HTML file for
.{0,180}(multiple accounts|more than one account|one account per|separate accounts).{0,180}. Alerting alone is worthless at that speed. Our first version only paged, and by the time a human read the notification the box was already thrashing, which is why the threshold kill went in.The memory sits in the child, and killing the child is enough. The sampler line one minute before that breach reads
OK: 10 claude process(es), largest 0.59 GiB. A minute later oneargv0=ugrepchild was at 10.13 GiB while the CLI parent that spawned it was still at 0.52 GiB. Killing the child recovered the machine every time and cost one search instead of the session. Soulimit -vin the snapshot'sgrep()before the re-exec would have turned all 11 of these into a cleanKilled, with no host impact and no lost work.Two things worth knowing if you are diagnosing this from outside.
commis the CLI version string for these processes, sodmesgandpsblame Claude Code itself, and the two OOM kills that started our investigation are still unattributed because nothing captured argv at the time. Capturing/proc/<pid>/cmdlineat the threshold is the entire diagnosis.The other one: the OOM is the loud failure. On the same box the same shim silently returns empty with exit 1 on a text file holding a single NUL byte (the
-Idefault, #56644), which is indistinguishable from "no matches". That variant is the one that actually produced a wrong answer here.We filed #78834 before finding this thread. Same bug, with an isolation table for the fixed-versus-variable bound question. Happy to close it as a duplicate of this one.
Independent confirmation on Claude Code 2.1.219 (Ubuntu 22.04 x86_64).
During an ordinary Bash tool call, Claude Code's shell snapshot shadowed
grepand re-executed the Claude Code binary as the bundledugrep. A two-sided bounded-repeat ERE on a 9 KB text file produced no result for approximately 12 minutes.The re-executed matcher reached approximately 3.33 GB anonymous RSS on a 3.8 GiB host, exhausted 2.3 GiB of swap, and triggered a
global OOM. The main Claude Code process was only approximately 152 MiB at the OOM snapshot. The resulting reclaim pressure stalled
unrelated interactive sessions and restarted systemd-journald via its watchdog.
Guarded reproduction using a fully synthetic fixture, a 512 MiB virtual-memory limit, and a five-second timeout:
The upstream issue Genivia/ugrep#549 confirmed the DFA-construction blow-up and landed a patch on July 19:
https://github.com/Genivia/ugrep/issues/549
https://github.com/Genivia/ugrep/commit/94c30f7dfd668b0567ca6474f58f0f0f5ef758b1
No fixed upstream release is available yet. Since Claude Code 2.1.219 still contains the affected path, please consider vendoring the
upstream patch, or adding a shim-side complexity/resource limit or fallback until a patched ugrep release can be bundled.
shim-side already described here https://github.com/anthropics/claude-code/issues/67021#issuecomment-5049415197
Thanks for the helpful pointer! We’ve already deployed a local workaround on our side. My comment was mainly intended to provide an independent confirmation and document that the affected path was still present in Claude Code 2.1.219.