Graphify-Labs/graphify · error · RuntimeError
could not load graph.json at {resolved_path}
Error message
could not load graph.json at {resolved_path} What it means
Raised by _GraphContextManager._load_entry (graphify/serve.py) when the shared _load_graph call exits via SystemExit while building a cache entry. In CLI usage invalid input terminates the process, but a client-supplied project_path in the shared MCP server must become a per-request RuntimeError so the server keeps serving other graphs. It always chains the original SystemExit as __cause__.
Source
Thrown at graphify/serve.py:121
"""Thread-safe graph contexts: one pinned default plus an LRU of projects."""
def __init__(self, max_contexts: int):
self._max_contexts = max_contexts
self._entries: OrderedDict[str, dict] = OrderedDict()
self._pinned: dict[str, dict] = {}
self._lock = threading.Lock()
def _load_entry(self, resolved_path: str, key: tuple[int, int]) -> dict:
"""Build one entry for an already-resolved path and known file key.
``_load_graph`` is also used by the CLI, where invalid input terminates
the process. A client-supplied ``project_path`` must instead become a
tool error, so the shared MCP server can continue serving other graphs.
"""
try:
graph = _load_graph(resolved_path)
except SystemExit as exc:
raise RuntimeError(f"could not load graph.json at {resolved_path}") from exc
# Warm the index before exposing the graph so its first query does not
# pay the expensive build cost.
_get_trigram_index(graph)
communities = _communities_from_graph(graph)
entry = {
"key": key,
"G": graph,
"communities": communities,
}
return entry
def load(self, resolved_path: str, *, pinned: bool = False) -> tuple[nx.Graph, dict[int, list[str]]]:
"""Return a fresh context, retaining project contexts by LRU order.
``resolved_path`` is resolved by the caller, making this method the
sole owner of file statting and cache-key construction.
``pinned=True`` is reserved for the server's configured default graph;View on GitHub (pinned to 7fe58b0b0f)
Solutions
- Inspect the __cause__ (the original SystemExit) or reproduce with graphify CLI on the same path to see the underlying reason.
- Fix the target project's graph: build it (/graphify), confirm graph.json exists, has .json suffix, and is under the size cap.
- As an MCP client, catch the tool error text and surface it to the user instead of retrying the same project_path.
- Server operators: validate project_path candidates (contains graphify-out/graph.json) before admitting them to the LRU.
Example fix
# before
try:
G, comms = ctx.load(resolved_path)
except RuntimeError as e:
pass # reason lost
# after
try:
G, comms = ctx.load(resolved_path)
except RuntimeError as e:
logger.error("load failed for %s: %s", resolved_path, e.__cause__) Defensive patterns
Strategy: try-catch
Validate before calling
def project_loadable(resolved_path: str) -> bool:
p = Path(resolved_path)
return p.suffix == ".json" and p.is_file() # extension + existence pre-checks Type guard
def is_admissible_project_path(resolved_path: str) -> bool:
p = Path(resolved_path)
return p.name == "graph.json" and p.is_file() Try / catch
try:
G, comms = ctx.load(resolved_path)
except RuntimeError as e:
if "could not load graph.json" in str(e):
cause = e.__cause__ # the SystemExit from _load_graph carries the real reason
logger.error("graph load failed for %s: %s", resolved_path, cause)
return tool_error(f"invalid graph at {resolved_path}: {cause}")
raise Prevention
- Validate project_path (graph.json exists, .json suffix, size under cap) before submitting to the shared server.
- Log __cause__, not just the RuntimeError text — the real reason is chained.
- One bad project should degrade to a tool error, never take the shared server down.
When it happens
Trigger: An MCP tool call with a project_path whose graph.json fails validation inside _load_graph (bad extension, missing file, over-cap size, or argparse-style sys.exit paths), hitting _load_entry's except SystemExit branch during LRU cache population.
Common situations: MCP clients sending project_path values that have no graphify-out/graph.json; concurrent requests to many projects where one has a broken/oversized graph; version drift where an older _load_graph still called sys.exit on bad input.
Related errors
- graph.json not found: {resolved_path}
- Graph base directory does not exist: {base}. Run /graphify f
- Path {path!r} escapes the allowed directory {base}. Only pat
- Graph file not found: {resolved}
- graph file {path} is {size:_d} bytes, exceeds {cap:_d}-byte
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/6c70a324eb961a13.
Report an issue: GitHub.