Graphify-Labs/graphify · error · FileNotFoundError

graph.json not found: {resolved_path}

Error message

graph.json not found: {resolved_path}

What it means

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.

Source

Thrown at graphify/serve.py:146

            "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;
        it remains warm without consuming a project-cache slot.
        """
        with self._lock:
            try:
                stat_result = Path(resolved_path).stat()
            except FileNotFoundError:
                raise FileNotFoundError(f"graph.json not found: {resolved_path}") from None
            key = (stat_result.st_mtime_ns, stat_result.st_size)
            entries = self._pinned if pinned else self._entries
            entry = entries.get(resolved_path)
            if entry is not None and entry["key"] == key:
                if not pinned:
                    self._entries.move_to_end(resolved_path)
                return entry["G"], entry["communities"]

            entry = self._load_entry(resolved_path, key)
            entries[resolved_path] = entry
            if not pinned:
                self._entries.move_to_end(resolved_path)
                while len(self._entries) > self._max_contexts:
                    self._entries.popitem(last=False)
            return entry["G"], entry["communities"]


def _strip_diacritics(text: str | None) -> str:

View on GitHub (pinned to 7fe58b0b0f)

Solutions

  1. Run the graph build in the target project so graphify-out/graph.json exists, then retry the tool call.
  2. Correct the project_path sent by the MCP client to the checkout that actually contains the graph.
  3. Guard concurrent rebuilds: build to a temp dir and atomically rename so readers never observe a missing graph.json.
  4. In long-lived servers, re-check existence on this error rather than keeping the dead path cached.

Example fix

# before
G, comms = ctx.load(str(project / "graphify-out" / "graph.json"))

# after
graph_json = project / "graphify-out" / "graph.json"
if not graph_json.exists():
    raise ToolError(f"No graph at {graph_json}; run /graphify in {project} first")
G, comms = ctx.load(str(graph_json))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def stat_graph(resolved_path: str) -> tuple[int, int] | None:
    try:
        st = Path(resolved_path).stat()
        return (st.st_mtime_ns, st.st_size)
    except FileNotFoundError:
        return None

if stat_graph(resolved_path) is None:
    raise ToolError(f"no graph.json at {resolved_path}; run /graphify first")

Type guard

def graph_present(resolved_path: str) -> bool:
    return Path(resolved_path).exists()

Try / catch

try:
    G, comms = ctx.load(resolved_path)
except FileNotFoundError as e:
    if "graph.json not found" in str(e):
        return tool_error("graph not built for this project; ask the user to run /graphify")
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14). Data as JSON: /api/errors/79ece0e22db77f49. Report an issue: GitHub.