{"record":{"id":"6c70a324eb961a13","repo":"Graphify-Labs/graphify","slug":"could-not-load-graph-json-at-resolved-path","errorCode":null,"errorMessage":"could not load graph.json at {resolved_path}","messagePattern":"could not load graph\\.json at (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"graphify/serve.py","lineNumber":121,"sourceCode":"    \"\"\"Thread-safe graph contexts: one pinned default plus an LRU of projects.\"\"\"\n\n    def __init__(self, max_contexts: int):\n        self._max_contexts = max_contexts\n        self._entries: OrderedDict[str, dict] = OrderedDict()\n        self._pinned: dict[str, dict] = {}\n        self._lock = threading.Lock()\n\n    def _load_entry(self, resolved_path: str, key: tuple[int, int]) -> dict:\n        \"\"\"Build one entry for an already-resolved path and known file key.\n\n        ``_load_graph`` is also used by the CLI, where invalid input terminates\n        the process. A client-supplied ``project_path`` must instead become a\n        tool error, so the shared MCP server can continue serving other graphs.\n        \"\"\"\n        try:\n            graph = _load_graph(resolved_path)\n        except SystemExit as exc:\n            raise RuntimeError(f\"could not load graph.json at {resolved_path}\") from exc\n        # Warm the index before exposing the graph so its first query does not\n        # pay the expensive build cost.\n        _get_trigram_index(graph)\n        communities = _communities_from_graph(graph)\n        entry = {\n            \"key\": key,\n            \"G\": graph,\n            \"communities\": communities,\n        }\n        return entry\n\n    def load(self, resolved_path: str, *, pinned: bool = False) -> tuple[nx.Graph, dict[int, list[str]]]:\n        \"\"\"Return a fresh context, retaining project contexts by LRU order.\n\n        ``resolved_path`` is resolved by the caller, making this method the\n        sole owner of file statting and cache-key construction.\n\n        ``pinned=True`` is reserved for the server's configured default graph;","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/serve.py#L103-L139","documentation":"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__.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ntry:\n    G, comms = ctx.load(resolved_path)\nexcept RuntimeError as e:\n    pass  # reason lost\n\n# after\ntry:\n    G, comms = ctx.load(resolved_path)\nexcept RuntimeError as e:\n    logger.error(\"load failed for %s: %s\", resolved_path, e.__cause__)","handlingStrategy":"try-catch","validationCode":"def project_loadable(resolved_path: str) -> bool:\n    p = Path(resolved_path)\n    return p.suffix == \".json\" and p.is_file()  # extension + existence pre-checks","typeGuard":"def is_admissible_project_path(resolved_path: str) -> bool:\n    p = Path(resolved_path)\n    return p.name == \"graph.json\" and p.is_file()","tryCatchPattern":"try:\n    G, comms = ctx.load(resolved_path)\nexcept RuntimeError as e:\n    if \"could not load graph.json\" in str(e):\n        cause = e.__cause__  # the SystemExit from _load_graph carries the real reason\n        logger.error(\"graph load failed for %s: %s\", resolved_path, cause)\n        return tool_error(f\"invalid graph at {resolved_path}: {cause}\")\n    raise","preventionTips":["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."],"tags":["mcp","graph","cache","error-handling"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}