Graphify-Labs/graphify · error · RuntimeError

Cannot read graph file {path}: {exc}. Re-run 'graphify extra

Error message

Cannot read graph file {path}: {exc}. Re-run 'graphify extract' to regenerate it.

What it means

The #479 shrink-guard in the Windows variant of the export step (tools/skillgen/expected/graphify__skill-windows.md:459). to_json() refuses to overwrite an existing graphify-out/graph.json that has more nodes than the newly built graph, returns False, and the script exits 1 with the '--force' hint. The report and analysis sidecar are only written after a successful graph write, keeping all artifacts consistent (#1392).

Source

Thrown at graphify/affected.py:293

            # The relation SITE in this node's file (call/import/reference line),
            # labeled by [via_relation] so it's never mistaken for a def line.
            location = f"{hit.via_file or data.get('source_file') or '-'}:{hit.via_location}"
        else:
            location = _format_location(data)  # honest fallback: the node's own def line
        lines.append(
            f"- {_node_label(graph, hit.node_id)} [{hit.via_relation}] {location}"
        )
    return "\n".join(lines)


def load_graph(path: Path) -> nx.Graph:
    import json
    from networkx.readwrite import json_graph

    try:
        raw = json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError) as exc:
        raise RuntimeError(
            f"Cannot read graph file {path}: {exc}. "
            "Re-run 'graphify extract' to regenerate it."
        ) from exc
    # Force directed so stored caller→callee direction survives the round-trip;
    # mirrors serve.py and __main__.py (#1174).
    raw = {**raw, "directed": True}
    # Normalize the edge key: graphify's `extract` output uses "edges" while
    # networkx's node_link_data default is "links". Without this, an edges-keyed
    # graph.json raises an uncaught KeyError: 'links' here — every other loader
    # (__main__.py) already normalizes this (#738; same class as #1198).
    if "links" not in raw and "edges" in raw:
        raw = dict(raw, links=raw["edges"])
    try:
        return json_graph.node_link_graph(raw, edges="links")
    except TypeError:
        return json_graph.node_link_graph(raw)

View on GitHub (pinned to 7fe58b0b0f)

Solutions

  1. If the corpus legitimately shrank, re-run a full build with --force so the smaller graph intentionally replaces graph.json.
  2. Otherwise diagnose the node-count regression: compare len(nodes) in the existing graph.json with the new G.number_of_nodes(); check .graphify_detect.json for skipped files.
  3. Verify the INPUT_PATH used by the PowerShell run matches the original full-build path.
  4. If graphify-out is inherited/stale, delete it and rebuild from scratch.

Example fix

# before
& (Get-Content graphify-out\.graphify_python) graphify update .
# ERROR: refused to shrink ... (#479)

# after
& (Get-Content graphify-out\.graphify_python) graphify build --force .
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

old = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8')).get('nodes', [])
new = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding='utf-8')).get('nodes') or []
if len(new) < len(old):
    print(f'shrink detected {len(old)} -> {len(new)}: use --force full rebuild if intentional')

Prevention

When it happens

Trigger: The PowerShell pipeline rebuilt a graph with fewer nodes than what is stored in the existing graphify-out/graph.json — e.g. an incremental update after files were deleted, a partial extraction that skipped files, or an old graph.json from a larger corpus still sitting in graphify-out/.

Common situations: graphify update after pruning the codebase; stale output directory from a previous project or branch with more files; PowerShell path handling causing a subset of files to be scanned; switching the target path without clearing graphify-out/.

Related errors


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