Skip to content

list_indexed_repositories hangs indefinitely (reproduces on clean state); separately, 1.0.9 upgrade duplicates every watch entry #70

Description

@hrabbach

Version: memtrace 1.0.9 (upgraded 2026-08-10 15:34 local; the prior version had been running since at least 08-04)
Platform: Windows 11 Pro 10.0.26200, Node v24.14.0, 16-core
MCP client: Claude Code (300 s tool idle timeout)
Backend: memdb, workspace store at C:\Users\<user>\Code\.memdb, 12 watched repos

Summary

The 1.0.9 upgrade re-registered every watched repository using Win32 extended-length (\\?\) paths without removing the pre-existing plain-path entries. watches.json now holds 24 entries for 12 repositories — each repo registered twice, under two spellings of the same directory.

After this, list_indexed_repositories never returns. The client aborts it at the 300 s idle timeout. Every per-repo scoped call against the same store in the same daemon returns instantly, so the database itself is fine — it is repository enumeration that is broken.

This is a total-outage failure mode, because list_indexed_repositories is documented as the mandatory first call and the scoped tools refuse to proceed without the repo_id it would return.

Evidence: the duplication is exactly 1:1 and timestamped to the upgrade

~/.memtrace/watches.json:

total watch entries : 24
distinct repo_ids   : 12
plain-path entries  : 12
\\?\-prefixed       : 12

Every one of the 12 repos appears exactly twice. Same repo, same branch, two path spellings:

{ "path": "C:\\Users\\<user>\\Code\\my-repo",
  "repo_id": "my-repo", "branch": "master",
  "registered_at": "2026-08-04T10:20:37.536446200+00:00", "origin": "manual" },

{ "path": "\\\\?\\C:\\Users\\<user>\\Code\\my-repo",
  "repo_id": "my-repo", "branch": "master",
  "registered_at": "2026-08-10T14:35:59.413590300+00:00", "origin": "manual" }

2026-08-10T14:35:59Z is the upgrade, to the second: the 1.0.9 binary has mtime 08-10 15:34:56 local and the daemon started 15:35:36 local (= 14:35:36 UTC). All 12 \\?\ entries were written in a 160 ms burst 23 s after daemon start.

runtime.json corroborates the switch — its workspaceMembers are now exclusively \\?\-prefixed, while the pre-existing watch entries are not.

Symptom 1 — list_indexed_repositories hangs, and never even logs

Task failed: MCP server "memtrace" tool "list_indexed_repositories" sent no response
or progress for 300s; aborting.

Reproduced twice, ~35 min apart, plus once independently by a separate agent process.

memcore-server.log contains no entry for any of these calls. Its last line is 15:19:41; the calls were at ~15:52 and ~16:55. The call does not reach a point where it logs anything — consistent with blocking on lock acquisition before doing any work. If enumeration takes a per-repo lock and the repo list contains the same store twice, a non-reentrant lock self-deadlocks on the second acquisition.

Symptom 2 — same DB, scoped call, instant

Immediately after the aborted call:

get_repository_stats(repo_id="my-repo")   → instant
{"_backend":"memdb","repo_id":"my-repo","branch":"master",
 "total_nodes":114222,"total_edges":234195,"community_count":415,
 "episode_count":655,"symbol_node_count":4039,"indexing_incomplete":false}

mem_diag also returns instantly. So: process-level calls fine, per-repo scoped reads fine, enumeration hangs forever.

Symptom 3 — 12.3 % of a core, sustained, for 25 hours

Daemon single instance on 127.0.0.1:3030
Uptime 25.4 h
CPU consumed 11,206 s (~12.3 % of one core, continuously)
RSS 493 MB

memcore-server.log shows index maintenance: pruned dead/cold rids from prop_index firing every ~5 minutes for 25 hours without pause, dropping up to 32,307 rids in a single pass:

14:39:16 dropped 500      15:14:39 dropped 1027
14:44:18 dropped 173      15:19:41 dropped 23799
13:58:57 dropped 32307    09:57:32 dropped 16263

24 watchers on 12 directories means every file save is ingested twice, which would produce exactly this churn. Note live_records: 2081697 against total_nodes: 114222 — ~18× more live records than nodes.

Possibly contributing — startup served traffic with structural indexes unbuilt

From the 08-10 boot:

WARN structural index fast-path unavailable on large store; deferring full iter_live repair
     until after startup
     db=memtrace live_records=2081697 wal_high_water_bytes=36637
     rid_uuid=false edge_adj=false count_store=true property_idx=false
WARN deferred structural index repair started; UI/API were already ready
WARN deferred primary record-index checkpoint started; UI/API were already ready
WARN deferred HNSW recovery incomplete vs durable VectorBlobs — repairing the missing vectors
     node_count=4449 live_vectorblobs=0

Three structural indexes reported unavailable while the API was already accepting requests. If enumeration depends on property_idx or rid_uuid and falls back to a full iter_live scan over 2.08 M records — twice, once per duplicate registration — that is another route to the same unbounded hang.

Impact

list_indexed_repositories is documented as:

ALWAYS call this first to discover available repo_ids — most other tools require a repo_id.

and the scoped tools refuse to infer one:

{"error_code":"repo_scope_required","scope_required":true,"scope_unresolvable":true,
 "diagnostic":{"message":"No explicit repo_id/scope was provided, and the current workspace does not identify one safe repository."}}

So an agent following the documented flow is fully blocked: discovery hangs for 300 s, then every downstream tool refuses for want of the repo_id it never got. Observed directly — an agent burned its full 300 s budget here and silently fell back to ripgrep, degrading its results without the user knowing why.

Workaround (confirmed)

Skip discovery; pass repo_id explicitly. It can be read from disk:

// <workspace>/.memdb/.memtrace-store-scope.json
{"version":1,"members":[{"repo_id":"my-repo","path":"c:/Users/<user>/Code/my-repo"}]}

All scoped tools then behave normally. Only usable by someone who knows the file exists.

Suggested fixes

  1. Make watch registration idempotent by canonical path. Normalise \\?\-prefixed and plain Windows paths to one canonical form before registering, and de-duplicate on upgrade. This is the root cause.
  2. Ship a one-time migration that collapses existing duplicate entries — users upgrading from pre-1.0.9 are already in this state and have no signal that anything is wrong.
  3. Bound list_indexed_repositories. It should fail fast with a diagnostic rather than hang unbounded, given it sits behind an "ALWAYS call this first" instruction.
  4. Emit progress events during enumeration so MCP idle timeouts do not fire on merely-slow work.
  5. Let scoped tools fall back to .memtrace-store-scope.json when enumeration is unavailable. Returning scope_unresolvable while that file names exactly one member turns a recoverable state into a fatal one.

Minor issues noticed while diagnosing

  • ~/.memtrace/last-run-version still reads 0.6.20 — never updated across many upgrades, including this one. If anything gates migrations on it, those migrations are not running.
  • Stale daemon.pid in the per-repo .memdb directory points at a PID from three days earlier that is no longer running, while the workspace-level .memdb/daemon.pid is correct.
  • Index 5 days stale. last_episode_time = 2026-08-06T23:13:40Z and last_indexed = null on 08-11, despite continuous work in a watched repo and 24 active watchers. indexed_node_count is null while total_nodes is populated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions