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 name Auditable == 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?
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
definitionspass: 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…use \Vendor\Pkg\Auditable as AuditableBase;(imported short nameAuditable== trait name)use AuditableBase;The extractor appears to ignore the
asalias and resolve the composed trait back to the same-short-name local trait — i.e. it seestrait AuditablecomposingAuditable(itself), a self-referential trait — then recurses without bound while expanding it to process a method body.This reproduces with
mode: fast, and withCBM_LSP_DISABLED=1andCBM_DISABLE_LSP_CROSS=1— so it is not in the LSP resolvers that received depth guards in #720. It is in thedefinitionspass 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: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\Auditabledoes 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):
Auditableuse AuditableBase;(import present but not composed){}(or no method at all)Note:
$thisis 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-auditingpackage, e.g.use OwenIt\Auditing\Auditable as AuditableBase;inside a localtrait 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
definitionspass:Anything else?
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.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.definitionspass, upstream of the LSP resolvers).