Skip to content

[bug] query_graph hangs on whole-graph OPTIONAL MATCH (no execution timeout / degradation) on large graphs #601

Closed

Description

@ecosuper2025

Version: codebase-memory-mcp v0.8.1
Project: real-world repo, 93,315 nodes / 488,306 edges

Summary

A query_graph Cypher that uses an unbounded OPTIONAL MATCH over the full node set does not return — it appears to hang rather than hit the documented 100k-row ceiling, because the optional expansion blows up before any rows are produced. There is no execution-time guard, so the caller just waits.

Repro

On a 90k+ node graph:

MATCH (a)
OPTIONAL MATCH (a)-[:CALLS]->(b)
RETURN a.qualified_name, count(b)

→ does not return in a reasonable time (no partial result, no error).

By contrast, a bounded/directed rewrite returns instantly:

MATCH (f:Function)-[:CALLS]->(g)
WHERE f.transitive_loop_depth >= 3
RETURN f.qualified_name, count(g)
ORDER BY count(g) DESC
LIMIT 20

Expected

  • query_graph should enforce an execution timeout (wall-clock), not only a row ceiling, and return a clear error (or partial result) instead of hanging.
  • Ideally: a planner guard that warns/aborts on unbounded OPTIONAL MATCH against the full graph, suggesting a directed/LIMITed rewrite.
  • Document the row ceiling vs time limit distinction in the tool description.

Workaround (consumer side)

Avoid whole-graph OPTIONAL MATCH: pre-filter the driving set with WHERE, use directed MATCH + count() for optional-cardinality, always LIMIT, and chunk by file_pattern for large sweeps. But a server-side timeout would prevent the foot-gun entirely.

Activity

  1. added
    bugSomething isn't working
    stability/performanceServer crashes, OOM, hangs, high CPU/memory
    cypherCypher query language parser/executor bugs
    on Jun 29, 2026
  2. DeusData commented on Jun 29, 2026

    @DeusData
    Owner

    Thanks. I’ve triaged this as Cypher stability/performance under #593 and #594. The likely fix needs execution guardrails, such as timeout/budgeting or bounded degradation for optional expansions before row materialization.

  3. rodmank commented on Jun 30, 2026

    @rodmank

    Independently corroborating on Linux / WSL2 (x86-64), v0.8.1 — same no-timeout/no-degradation behavior, and notably it triggers even on a small graph, not just 90k+ nodes.

    Project: 5,996 nodes / 14,201 edges, via codebase-memory-mcp cli query_graph (fresh process per call):

    MATCH (n)-[:CALLS*]->(m)-[:CALLS*]->(k) RETURN count(*) AS c

    → completes but takes 8.5 s wall / 493 MB RSS (vs sub-ms for bounded depth-3 paths). Cost grows unboundedly well before the 100k-row ceiling, consistent with the missing execution-time guard described here.

    Unsupported-syntax constructs (EXISTS {…}, RETURN p path variables) return clean errors immediately — so the foot-gun is specifically unbounded OPTIONAL MATCH / variable-length-path / combinatorial joins, not parsing.

    Client-side impact: when such a query exceeds the memory budget the server process dies, which over the MCP stdio transport surfaces as MCP error -32000: Connection closed. The client (Claude Code) auto-reconnects (~12 ms) and retries the same query, which crashes again — a reconnect storm that to the end user looks exactly like "the server hangs / doesn't answer." A server-side wall-clock timeout + clean error (as proposed here) would resolve both the hang and the reconnect storm.

    +1 for the execution-time guard and the planner warning on unbounded whole-graph expansion.

  4. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Jun 30, 2026
  5. rudi193-cmd commented on Jul 5, 2026

    @rudi193-cmd
    Contributor

    Consumer-side mitigation pattern (Willow fleet audit)

    We hit the crash/hang class during a June structural audit and shipped a bounded wrapper (sap/cbm_facade.py in willow-2.0) that:

    1. Rejects unbounded / high-risk Cypher (count(*), coalesce(), <-, etc.) before it reaches the server
    2. Auto-appends LIMIT when missing on query_graph
    3. Subprocess timeout (WILLOW_CBM_TIMEOUT_S, default 30s) so a wedged query does not kill the IDE stdio session

    Dead-code style queries should use forward-only MATCH (caller)-[:CALLS]->(f) with explicit LIMIT, not whole-graph OPTIONAL MATCH anti-joins — matches the bounded query in #627.

    Not a substitute for server-side timeout/degradation, but documents what downstream integrators are doing until #601/#627 are fixed. Related: #873 (DISTINCT ordering still broken on v0.8.1 despite #237).

  6. added this to the 0.9.1-rc milestone on Jul 8, 2026
  7. added a commit that references this issue on Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcypherCypher query language parser/executor bugspriority/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