Repository navigation
Description
Version
codebase-memory-mcp 0.8.1
Platform
macOS (Apple Silicon)
Install channel
GitHub release archive / install.sh / install.ps1
Binary variant
ui
What happened, and what did you expect?
A single ~6-line PHP trait drives the indexer into unbounded memory growth during the definitions pass: RSS climbs to ~50% of machine RAM (~18–20 GB on a 36 GB Mac) and the process is OOM-killed (SIGKILL, exit 137).
The trigger is a name collision between a trait and an aliased use … as … import that the trait composes:
trait Auditable…- … that does
use \Vendor\Pkg\Auditable as AuditableBase;(imported short nameAuditable== trait name) - … and composes it with
use AuditableBase; - … and has at least one method with a non-empty body.
The extractor appears to ignore the as alias and resolve the composed trait back to the same-short-name local trait — i.e. it sees trait Auditable composing Auditable (itself), a self-referential trait — then recurses without bound while expanding it to process a method body.
This reproduces with mode: fast, and with CBM_LSP_DISABLED=1 and CBM_DISABLE_LSP_CROSS=1 — so it is not in the LSP resolvers that received depth guards in #720. It is in the definitions pass and needs its own recursion/expansion guard.
Expected: the file indexes instantly (it's 6 lines), as it does the moment any one of the three ingredients is removed.
Reproduction
No proprietary code needed. Create a directory with one file Auditable.php:
<?php
namespace App\Demo;
use Vendor\Pkg\Auditable as AuditableBase; // aliased import; short name "Auditable" == trait name below
trait Auditable {
use AuditableBase; // composes the aliased trait
function f() { $x = 1; return $x; } // any method with a non-empty body
}Run:
CBM_LSP_DISABLED=1 codebase-memory-mcp cli index_repository \
'{"repo_path":"/path/to/that/dir","mode":"fast"}'→ RSS climbs to ~18–20 GB and the process is killed (exit 137). Vendor\Pkg\Auditable does not need to exist — it is an unresolved, pure short-name collision.
All three ingredients are required. Removing any one makes it index instantly at ~0 GB (verified, each run monitored for peak RSS):
| Change from the repro above | Result |
|---|---|
rename the trait so its name ≠ Auditable |
✅ OK, ~0 GB |
drop use AuditableBase; (import present but not composed) |
✅ OK, ~0 GB |
empty method body {} (or no method at all) |
✅ OK, ~0 GB |
| all three present (as above) | ❌ OOM, ~20 GB, exit 137 |
Note: $this is not required — a body of just $x = 1; is enough.
Real-world origin: this is the shape of a trait that wraps the popular owen-it/laravel-auditing package, e.g. use OwenIt\Auditing\Auditable as AuditableBase; inside a local trait Auditable. So it hits real Laravel apps, not just synthetic input.
Logs
From the minimal single-file repro above — it dies right at the start of the definitions pass:
level=info msg=mem.init budget_mb=18432 total_ram_mb=36864
level=info msg=pipeline.discover files=1 elapsed_ms=0
level=info msg=pipeline.route path=full
level=info msg=pass.start pass=structure files=1
level=info msg=pass.done pass=structure nodes=2 edges=1
level=info msg=pass.timing pass=structure elapsed_ms=0
level=info msg=pipeline.mode mode=sequential files=1
level=info msg=pkgmap.scan_repo manifests=0
level=info msg=pkgmap.scan manifests_from_files=0 manifests_from_walk=0 entries=0
level=info msg=pass.start pass=definitions files=1
<no further output — SIGKILL / OOM, exit 137>
Anything else?
- Found by bisecting a private Laravel 10 app (~1500 files) that always OOM-killed at indexing, down to this one trait file.
laravel/framework@ HEAD (3020 files, 40 784 nodes, 223 117 edges) indexes fine in ~3.7 s at ~1 GB RSS — so this is not a scale/large-repo problem, it's this specific construct. - Independent of
mode(fast/moderate),CBM_WORKERS(tried 2 and 14), and LSP flags. Peak RSS is always ~budget_mb(50% of RAM), which is why it OOMs even on an otherwise-idle 36 GB machine. - Likely related: [bug] PHP: framework/core method calls ($storage->load()) become false-positive CALLS edges to same-named project methods — same class as the (fixed) Perl #476 #606 (PHP same-short-name collisions) and py_lsp: py_eval_expr_type infinite recursion — Python missed in 9f981a1 depth-guard fix #720 (LSP depth guard missing for PHP — but note this crash is in the
definitionspass, upstream of the LSP resolvers).
Activity
DeusData commentedon Jul 3, 2026
Thanks for reducing this to the trait/import-alias collision. Triage: high-priority PHP parser stability and extraction correctness under #390/#592.
The expected behavior is alias-aware trait composition, or at worst an
unknown/skipped relation. It must not recurse without bound. The first guard should be a tiny PHP fixture matching this collision and a subprocess/OOM-safe regression so the test runner is not killed by the bug.Merge pull request #986 from DeusData/fix/php-trait-alias-oom-guards
DeusData commentedon Jul 9, 2026
Regression guards for this landed on main in #986 (merge f7127ee). The underlying fix shipped with #920's trait-alias guards in v0.9.0; the new test reproduces the exact name-collision hang from this report (verified RED against the unguarded code) and now pins it green permanently.
test(php-lsp): regression guards for aliased-trait name-collision OOMs
KasaharaDefries commentedon Jul 16, 2026
The v0.9.0 release binary still reproduces this (macOS
darwin-arm64, checksum-verified from the release page), so #920's guards don't seem to cover this variant — here the collision is with the alias target's short name, not the alias itself:Symptoms on 0.9.0:
parallel.extract.progressstalls at N-4 of N every run; worker RSS grows until the memory-budget kill (SIGKILL at ~16–18 GB withbudget_mb=18432). Reproduced 3/3 runs; also reproduces withCBM_INDEX_WORKER_TIMEOUT_S=1800, so it's the budget kill, not the watchdog timeout.sampleof the spinning worker thread:.cbmignore→ the same repo (~1760 files) indexes in 1.7 s / 709 MB RSS.Found the culprit by diffing
CBM_LSP_DEBUG=1per-module output against the discovered file list. Happy to test a main build or provide more detail if useful.DeusData commentedon Jul 18, 2026
Reopening based on the detailed post-close v0.9.0 reproduction. The alias target’s short name colliding with the local trait name is a distinct unbounded-recursion variant that the earlier guard did not cover: the definitions pass stalls, memory grows to the worker budget, and excluding the single trait restores a bounded run. This is back in the 0.9.1-rc queue under #390. The RED guard should preserve the exact
AuditableBase/Auditablebinding shape and prove bounded completion without relying on the worker budget kill.DeusData commentedon Sep 26, 2026
Thank you, @KasaharaDefries, for bisecting a whole Laravel app down to one trait, and for the precise sample of
flatten_trait_into_class!We rebuilt your v0.9.0 shape on current main:
use OwenIt\Auditing\Auditable as AuditableBase;insidetrait Auditable { use AuditableBase; … };It now indexes in about 5 seconds under a 1 GB memory budget, with no runaway growth. Could you confirm on v0.11.0 with the real repo, without the
.cbmignoreexclusion? If extract progress still stalls at N-4, a freshsampleof the worker would tell us which path is still unbounded.github-actions commentedon Sep 26, 2026
Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:
codebase-memory-mcp --versionyou're onOnce that's here we'll pick it straight back up. Heads-up: issues left
awaiting-reporterare automatically closed after a few weeks of silence, but a comment reopens the door anytime.github-actions commentedon Oct 18, 2026
This issue has been waiting on more information for 21 days (a version, exact steps, or a public repro), so it's now marked
stale. It will not be closed automatically — a maintainer reviews stale issues by hand. Adding a comment with the details clears the label and puts it straight back in the queue. We're happy to pick it back up the moment we can reproduce it.fix(php): index trait methods by receiver while flattening (DeusData#…
DeusData commentedon Oct 19, 2026
Thank you again for isolating the alias-target collision. Checking back on the September retest request: if you have safely tried a newer build, did the exact trait shape still stall? Please do not repeat an unbounded run just to keep this report open; I have no fresh real-repository confirmation to add.
DeusData commentedon Oct 25, 2026
Thank you again for the report. Any existing safe retest result for the aliased-trait case is welcome; please do not repeat an unbounded OOM run just for this follow-up.