{"record":{"id":"79ece0e22db77f49","repo":"Graphify-Labs/graphify","slug":"graph-json-not-found-resolved-path","errorCode":null,"errorMessage":"graph.json not found: {resolved_path}","messagePattern":"graph\\.json not found: (.+?)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"graphify/serve.py","lineNumber":146,"sourceCode":"            \"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;\n        it remains warm without consuming a project-cache slot.\n        \"\"\"\n        with self._lock:\n            try:\n                stat_result = Path(resolved_path).stat()\n            except FileNotFoundError:\n                raise FileNotFoundError(f\"graph.json not found: {resolved_path}\") from None\n            key = (stat_result.st_mtime_ns, stat_result.st_size)\n            entries = self._pinned if pinned else self._entries\n            entry = entries.get(resolved_path)\n            if entry is not None and entry[\"key\"] == key:\n                if not pinned:\n                    self._entries.move_to_end(resolved_path)\n                return entry[\"G\"], entry[\"communities\"]\n\n            entry = self._load_entry(resolved_path, key)\n            entries[resolved_path] = entry\n            if not pinned:\n                self._entries.move_to_end(resolved_path)\n                while len(self._entries) > self._max_contexts:\n                    self._entries.popitem(last=False)\n            return entry[\"G\"], entry[\"communities\"]\n\n\ndef _strip_diacritics(text: str | None) -> str:","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/serve.py#L128-L164","documentation":"Raised by _GraphContextManager.load (graphify/serve.py) when Path(resolved_path).stat() raises FileNotFoundError while constructing the (mtime_ns, size) cache key. The message re-raises with `from None` for a clean chain, since the stat failure already tells the whole story: there is no graph.json at the resolved location. This is the concurrency-safe variant of the missing-file check — it catches the file disappearing between request and stat.","triggerScenarios":"Calling ctx.load(resolved_path) on an MCP tool request whose project_path resolves to a checkout without graphify-out/graph.json, or where the file was deleted by a concurrent rebuild/clean between a prior successful load and this one.","commonSituations":"MCP client points at a project that has never run /graphify; git clean or CI wipe removing graphify-out mid-session; hot-reload racing a destructive rebuild; stale IDE MCP config after the repo moved.","solutions":["Run the graph build in the target project so graphify-out/graph.json exists, then retry the tool call.","Correct the project_path sent by the MCP client to the checkout that actually contains the graph.","Guard concurrent rebuilds: build to a temp dir and atomically rename so readers never observe a missing graph.json.","In long-lived servers, re-check existence on this error rather than keeping the dead path cached."],"exampleFix":"# before\nG, comms = ctx.load(str(project / \"graphify-out\" / \"graph.json\"))\n\n# after\ngraph_json = project / \"graphify-out\" / \"graph.json\"\nif not graph_json.exists():\n    raise ToolError(f\"No graph at {graph_json}; run /graphify in {project} first\")\nG, comms = ctx.load(str(graph_json))","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef stat_graph(resolved_path: str) -> tuple[int, int] | None:\n    try:\n        st = Path(resolved_path).stat()\n        return (st.st_mtime_ns, st.st_size)\n    except FileNotFoundError:\n        return None\n\nif stat_graph(resolved_path) is None:\n    raise ToolError(f\"no graph.json at {resolved_path}; run /graphify first\")","typeGuard":"def graph_present(resolved_path: str) -> bool:\n    return Path(resolved_path).exists()","tryCatchPattern":"try:\n    G, comms = ctx.load(resolved_path)\nexcept FileNotFoundError as e:\n    if \"graph.json not found\" in str(e):\n        return tool_error(\"graph not built for this project; ask the user to run /graphify\")\n    raise","preventionTips":["Stat the graph.json path before each session that will query it.","Make rebuilds atomic (build to temp, rename) so stat never observes a gap during hot-reload.","Drop dead project paths from client configs after moving checkouts."],"tags":["mcp","graph","missing-file","concurrency"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}