Skip to content

Indexer OOM-killed (exit 137): infinite loop in PHP trait flattening when a class shares its short name with an aliased trait import #951

Description

@tmstack-io

Summary

Indexing a repository containing a specific — and perfectly valid — PHP pattern makes an extract worker allocate memory without bound (observed 29 GB RSS on a single worker, ~100 GB system-wide including swap/compression before the memory watchdog SIGKILLs the process group). The run always dies with exit 137 shortly before extraction completes, because the affected files are the slowest and finish last.

A stack sample of the ballooning worker shows it spinning inside the trait flattener:

cbm_extract_file
  cbm_run_php_lsp
    php_lsp_collect_class_fields
      process_class_for_fields
        flatten_trait_into_class   <- ~100% of samples, allocating via cbm_arena_strdup/cbm_arena_sprintf

Root cause (from behavior)

Trait use resolution appears to ignore namespaces and aliases and resolve by short name. When a class has the same short name as a trait it imports under an alias, the resolver picks the class itself as the trait to flatten, and flatten_trait_into_class recurses/loops forever while copying fields.

Minimal reproduction (2 files, 15 lines)

src/delivery-trait.php:

<?php
namespace App\Functions;

trait Delivery {
	public function send( string $payload ): bool {
		return true;
	}
}

src/delivery-class.php:

<?php
namespace App\Modules\Foo;

use App\Functions\Delivery as Delivery_Trait;

class Delivery {
	use Delivery_Trait;

	public function run(): void {
	}
}

Steps:

mkdir -p repro/src && cd repro && git init
# add the two files above
codebase-memory-mcp cli index_repository '{"repo_path": "<abs path>/repro", "mode": "fast"}'

Result: the worker's RSS grows until the memory watchdog kills the process group; the CLI exits with code 137. Remove the alias (or rename the class so it no longer collides with the trait's short name) and the same repo indexes in milliseconds.

Note this is idiomatic PHP: use Vendor\Traits\Delivery as Delivery_Trait; inside class Delivery is a common way to disambiguate, and PHP itself resolves it correctly.

Environment

  • codebase-memory-mcp v0.8.1 (latest release at time of writing)
  • macOS (Darwin 25.5.0), arm64, 64 GB RAM
  • Real-world hit: a WordPress codebase where three module classes named Delivery import a shared trait Delivery from another namespace via use ... as Delivery_Trait;

Impact / workaround

  • MCP-server-driven index_repository takes the whole MCP server down with it when the watchdog fires.
  • CBM_WORKERS=1, CBM_DISABLE_LSP_CROSS=1 and CBM_LSP_DISABLED=1 do not prevent it (tested).
  • Current workaround: exclude the colliding class files via .cbmignore.

Suggested fixes

  1. Resolve trait use statements through the file's import table (aliases + namespaces) instead of short names, and never resolve a use inside a class body to a class.
  2. As a safety net, add a depth/visited-set guard in flatten_trait_into_class so any resolution cycle degrades gracefully instead of allocating until the watchdog kills the run.

May relate to the memory reports in #581 / #593 / #832, but this one is a deterministic infinite loop with a tiny repro rather than gradual growth/retention.

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

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions