Repository navigation
Description
Summary
When the grouping key in RETURN is a function call rather than a property, the executor does not group. It returns a single row and writes the aggregate value into the non-aggregate column. No error is raised.
Repro
query_graph --query "MATCH (n) RETURN labels(n) AS label, count(*) AS c ORDER BY c DESC"
Actual:
{"columns":["label","c"],"rows":[["21143","21143"]]}Expected: one row per label — ["Function", 14960], ["Variable", 1747], ["Macro", 1066], … (these counts are correct and are what get_graph_schema reports).
What does work
labels(n)as a plain projection:MATCH (n:Function) RETURN labels(n) LIMIT 1->["Function"]- Property-based grouping:
MATCH (n) RETURN n.file_path, count(*)-> correct per-file counts
Both halves work in isolation; only the combination fails.
Why this matters
The failure is silent. The caller receives a well-formed result with a plausible-looking number in it, so an LLM agent consuming this reports the wrong answer confidently. The engine already rejects unsupported constructs loudly and correctly (CREATE -> unsupported Cypher feature: CREATE clause); an unsupported ... error here would be strictly better than a wrong row.
Environment
- Version: codebase-memory-mcp 0.9.0 (release binary, darwin-arm64)
- OS: macOS 26.5.2, Apple Silicon
- Repo under test: DeusData/codebase-memory-mcp @ af3ffbd (21,143 nodes / 123,103 edges)
- Verified identically through both the MCP server and
climode (outputs byte-identical)
Activity
DeusData commentedon Jul 28, 2026
Thanks for the scalar-function grouping reproduction.
Current main distinguishes aggregate functions with is_aggregate_func (src/cypher/cypher.c:3621), but the grouping key is built through the separate projection path at src/cypher/cypher.c:3719-3748. That split is consistent with a function expression being classified/projected incorrectly and collapsing rows into one group.
PR #1279 appears to target this exact execution path and is queued for review; it is not pre-approved by triage. The issue is routed to 0.9.1-rc at priority/high because the result is silently wrong. Thank you for making the expected groups explicit.
artaommahe commentedon Jul 30, 2026
Confirming this on 0.9.1-rc.1 (macOS arm64, standard variant, GitHub release archive), and adding three findings from a duplicate I filed and have since closed (#1358).
1. The trigger is broader than "the grouping key is a function call". Every function-call column in an aggregating
RETURNis dropped from the group key and overwritten with the aggregate value — even when grouping succeeds correctly on a different, property-based column in the same query:So it isn't "grouping fails when the key is a function" so much as "function columns are excluded from the key and then written over".
2. Two smaller repros showing the function value evaluates to empty inside an aggregate, which may be the more direct handle on it:
DISTINCT type(r)without an aggregate is fine and returns the correct distinct set, so the defect is confined to the aggregate path.3. A workaround worth documenting for users — materialise the function value through
WITHbefore aggregating. Verified correct on both a 58-edge toy graph and a 120,805-edge TypeScript monorepo (23 correct rows there):Variants I checked that make no difference to the bug:
count(r)vscount(*), presence or absence ofORDER BY, presence or absence of the alias, andlabels(n)vstype(r)— all eight combinations fail identically.For what it's worth on severity: this is the natural first query to write against an unfamiliar graph ("what edge types exist and how many of each"), and README.md's query-language section lists
labels/typeas supported functions andcountas a supported aggregate, then promises that anything outside the supported subset "fails with a clearunsupported …error rather than returning empty results". A silently wrong row is the one outcome that contract rules out.Small repro repo if useful: https://github.com/artaommahe/codebase-memory-mcp-rc-repros (Case E).
Posted by Claude Code on behalf of @artaommahe
DeusData commentedon Sep 19, 2026
Thank you for separating scalar grouping from functions inside aggregates and supplying the WITH workaround. Current grouping code has changed to classify actual aggregates rather than every function column, but that does not prove
count(DISTINCT type(r))orcollect(type(r))correct. Those examples must remain separate acceptance checks; I cannot claim the whole issue fixed from this inspection.DeusData commentedon Sep 26, 2026
Thank you, @melihemreguler, for the grouping reproduction, and @artaommahe for the extra cases and the
WITHworkaround!This is fixed as of v0.10.0 (b43bddd). On v0.11.0:
MATCH (n) RETURN labels(n) AS label, count(*) AS creturns one row per label.RETURN type(r) AS t, a.name AS n, count(*)groups on both columns without overwritingt.count(DISTINCT type(r))andcollect(type(r))are now rejected with a clear error instead of returning misleading values.WITH type(r) AS t RETURN t, count(*)remains the supported form.Could you confirm on v0.11.0? We'll close this once you do.
github-actions commentedon Sep 26, 2026
Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:
codebase-memory-mcp --versionyou're onOnce that's here we'll pick it straight back up. Heads-up: issues left
awaiting-reporterare automatically closed after a few weeks of silence, but a comment reopens the door anytime.DeusData commentedon Oct 19, 2026
Thank you again for the scalar-grouping and aggregate-expression examples. The latest update distinguishes corrected grouping from unsupported function expressions inside aggregates and asks for v0.11.0 confirmation. I have no new result from your environment to add.
DeusData commentedon Oct 25, 2026
Thank you again for the grouping and aggregate-expression cases. Could you confirm the corrected grouping and explicit unsupported-expression behavior on the requested version?