Summary
The MCP server crashes with SIGABRT (malloc: pointer being freed was not allocated) on the second index_repository call within one server process, whenever both indexed directories contain roughly 30+ source files. The process dies silently — nothing on stderr — so the MCP client just sees MCP error -32000: Connection closed, and all codebase-memory tools are gone for the rest of the client session.
This makes any long-lived session that indexes two projects (or re-indexes one) kill the server. It likely explains "server randomly disconnected" reports, since the first index works and the crash only hits the next one.
Environment
- codebase-memory-mcp v0.10.0, prebuilt release binary
- macOS arm64 (Darwin 25.5.0, M-series), 8 GB RAM (
mem.init budget_mb=4096 total_ram_mb=8192)
- Client: Claude Code via stdio transport (also reproduces with no client at all — see repro)
Crash report (macOS .ips, faulting thread)
Five separate crashes across three days, all with this identical stack:
__pthread_kill
pthread_kill
abort
malloc_vreport
malloc_report
___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED
ts_stack_delete
ts_parser_delete
cbm_destroy_thread_parser
extract_worker
cbm_parallel_for
cbm_parallel_extract
cbm_pipeline_run
handle_index_repository
cbm_mcp_server_handle
So an extract_worker in the second pipeline run tears down its thread parser and ts_stack_delete frees a pointer the allocator doesn't own — looks like a per-thread parser (or its ts_stack) surviving from the first run and being destroyed again, or being freed against the wrong allocator/arena after the first run's cleanup.
Deterministic repro (no client needed, ~30 s)
#!/bin/bash
# Generates two unrelated dirs of tiny .py files, then indexes both in ONE server process.
# First index succeeds; the second crashes the server (5/5 runs on my machine).
BASE=$(mktemp -d)
mkdir -p "$BASE/dirA" "$BASE/dirB"
for i in $(seq 1 30); do
printf 'class Handler%s:\n def run(self, x):\n return self.helper(x) + %s\n def helper(self, x):\n for i in range(10):\n x += i\n return x\n\ndef main_%s():\n return Handler%s().run(%s)\n' \
$i $i $i $i $i > "$BASE/dirA/mod$i.py"
done
for i in $(seq 1 60); do
printf 'def util_%s(y):\n return y * %s\n' $i $i > "$BASE/dirB/util$i.py"
done
{
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"repro","version":"1.0"}}}'
printf '%s\n' '{"jsonrpc":"2.0","method":"notifications/initialized"}'
sleep 0.3
printf '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"index_repository","arguments":{"repo_path":"%s/dirA","mode":"full"}}}\n' "$BASE"
sleep 4
printf '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"index_repository","arguments":{"repo_path":"%s/dirB","mode":"full"}}}\n' "$BASE"
sleep 15
} | codebase-memory-mcp
Expected: two "status":"indexed" results.
Actual: response for id 2 only; the process aborts (Abort trap: 6) ~1–2 s after the id 3 call, before any response. macOS writes a crash report to ~/Library/Logs/DiagnosticReports/codebase-memory-mcp-*.ips with the stack above.
What I ruled out (all with the same binary, fresh process each)
| Scenario |
Result |
| Single index of either dir (any size, incl. a 236-file mixed-language repo) |
✅ OK |
| 2nd index in a new process (DBs from run 1 on disk) |
✅ OK |
| Tiny pair: 1-file dir then 2-file dir |
✅ OK (too small to trigger) |
| Tiny first index (2 files) → large second (236 files) |
✅ OK |
| ~30-file dir → ~60-file dir, same process |
💥 SIGABRT |
| Same at larger sizes, nested or unrelated dirs, pure Python or mixed languages |
💥 SIGABRT |
So it needs both runs big enough (my guess: enough files that cbm_parallel_for spins up multiple/reused worker threads), and on-disk DB state is irrelevant — it's purely in-process state carried from the first extract into the second.
Happy to attach the full .ips crash reports or test a patched binary.
Summary
The MCP server crashes with
SIGABRT(malloc: pointer being freed was not allocated) on the secondindex_repositorycall within one server process, whenever both indexed directories contain roughly 30+ source files. The process dies silently — nothing on stderr — so the MCP client just seesMCP error -32000: Connection closed, and allcodebase-memorytools are gone for the rest of the client session.This makes any long-lived session that indexes two projects (or re-indexes one) kill the server. It likely explains "server randomly disconnected" reports, since the first index works and the crash only hits the next one.
Environment
mem.init budget_mb=4096 total_ram_mb=8192)Crash report (macOS .ips, faulting thread)
Five separate crashes across three days, all with this identical stack:
So an
extract_workerin the second pipeline run tears down its thread parser andts_stack_deletefrees a pointer the allocator doesn't own — looks like a per-thread parser (or itsts_stack) surviving from the first run and being destroyed again, or being freed against the wrong allocator/arena after the first run's cleanup.Deterministic repro (no client needed, ~30 s)
Expected: two
"status":"indexed"results.Actual: response for id 2 only; the process aborts (
Abort trap: 6) ~1–2 s after the id 3 call, before any response. macOS writes a crash report to~/Library/Logs/DiagnosticReports/codebase-memory-mcp-*.ipswith the stack above.What I ruled out (all with the same binary, fresh process each)
So it needs both runs big enough (my guess: enough files that
cbm_parallel_forspins up multiple/reused worker threads), and on-disk DB state is irrelevant — it's purely in-process state carried from the first extract into the second.Happy to attach the full
.ipscrash reports or test a patched binary.