Skip to content

fix(serve): route node endpoints by query param so file nodes work - #37

Merged
cvetty merged 1 commit into
mainfrom
fix/serve-node-query-param
Jul 28, 2026
Merged

fix(serve): route node endpoints by query param so file nodes work#37
cvetty merged 1 commit into
mainfrom
fix/serve-node-query-param

Conversation

@cvetty

@cvetty cvetty commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Bug

Clicking a file node in the playground tree (e.g. src/whygraph/mcp/path_history.py) showed
The string did not match the expected pattern. in the detail panel, with no tabs. Function/method
nodes worked fine.

Cause

A CodeGraph file node's qualified_name is a path with slashes (src/whygraph/mcp/path_history.py),
whereas a function's is dotted and slash-free. The client requested
GET /api/node/src%2Fwhygraph%2Fmcp%2Fpath_history.py; uvicorn decodes %2F/, so the path becomes
/api/node/src/whygraph/mcp/path_history.py, which no longer matches the single-segment
/api/node/{qualified_name} route. It then fell through to the SPA catch-all, which returned
index.html with 200 OK. The client saw res.ok, tried res.json() on HTML, and that parse failure
surfaced as the cryptic message — with no data, hence no tabs.

Fix

Move the whole /node family (detail, rationale GET/POST, evidence) from a path segment to a
qualified_name query parameter — the same approach already used for the slash-containing
/api/history?path=. Slash-containing identifiers now resolve cleanly:

  • GET /api/node?qualified_name=…
  • GET|POST /api/node/rationale?qualified_name=…
  • GET /api/node/evidence?qualified_name=…

Also hardened the client: a content-type guard in parse() turns any future non-JSON 200 (e.g. an
unmatched /api route falling through to the SPA) into a clear ApiError instead of a JSON-parse crash.

Verified

  • New regression test: GET /api/node?qualified_name=src/pkg/a.py (a file node) returns 200
    application/json with kind: file and its children — no SPA fall-through.
  • Confirmed live against this repo's real .codegraph: the file-node detail now returns JSON with the
    file's imports + functions as children.
  • Full suite: 559 tests pass; ruff check + ruff format --check clean.

Note

The three commits (whygraph serve feature + docs) already merged via #36. This is a follow-up fix that
briefly landed directly on main; it has been reverted on main (commit 3cb6c18) and re-submitted
here for review.

A CodeGraph `file` node's qualified_name is a path with slashes (e.g.
"src/whygraph/mcp/path_history.py"). Sent as a path segment to
`/api/node/{qualified_name}`, uvicorn decodes %2F to `/`, the single-segment
route stops matching, and the request falls through to the SPA catch-all —
returning index.html with a 200. The client then tries to JSON-parse HTML and
the panel shows "The string did not match the expected pattern." with no tabs.

Move the whole /node family (detail, rationale GET/POST, evidence) to a
`qualified_name` query parameter — the same approach already used for
/api/history — so slash-containing identifiers resolve cleanly. Add a
content-type guard in the client so any future non-JSON 200 surfaces as a clear
ApiError instead of a cryptic parse crash. Regression test covers a file node.
@cvetty cvetty self-assigned this Jul 28, 2026
@cvetty cvetty added bug Something isn't working enhancement New feature or request and removed bug Something isn't working enhancement New feature or request labels Jul 28, 2026
@cvetty
cvetty merged commit d1965b7 into main Jul 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant