You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[bug] query_graph hangs on whole-graph OPTIONAL MATCH (no execution timeout / degradation) on large graphs #601
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)
OPTIONALMATCH (a)-[:CALLS]->(b)
RETURNa.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)
WHEREf.transitive_loop_depth>=3RETURNf.qualified_name, count(g)
ORDER BYcount(g) DESCLIMIT20
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.
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.
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) RETURNcount(*) ASc
→ 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.
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:
Rejects unbounded / high-risk Cypher (count(*), coalesce(), <-, etc.) before it reaches the server
Auto-appends LIMIT when missing on query_graph
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-onlyMATCH (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).
Activity
DeusData commentedon Jun 29, 2026
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.
rodmank commentedon Jun 30, 2026
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):→ 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 ppath variables) return clean errors immediately — so the foot-gun is specifically unboundedOPTIONAL 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.
rudi193-cmd commentedon Jul 5, 2026
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.pyin willow-2.0) that:count(*),coalesce(),<-, etc.) before it reaches the serverLIMITwhen missing onquery_graphWILLOW_CBM_TIMEOUT_S, default 30s) so a wedged query does not kill the IDE stdio sessionDead-code style queries should use forward-only
MATCH (caller)-[:CALLS]->(f)with explicitLIMIT, not whole-graphOPTIONAL MATCHanti-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).
Merge pull request #1109 from DeusData/fix/601-cypher-exec-deadline
fix(cypher): add a wall-clock execution deadline to abort runaway que…
fix(cypher): add a wall-clock execution deadline to abort runaway que…
fix(cypher): expansion materializes every matched row — the cap falsi…