Graphify-Labs/graphify · error · SystemExit
ERROR: refused to shrink graphify-out/graph.json (existing g
Error message
ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).
What it means
Graphify's export.to_json() implements the #479 shrink-guard: it returns False and writes nothing when the newly built graph has fewer nodes than the existing graphify-out/graph.json. The skill.md Step 4 snippet checks that return value and exits with this error BEFORE writing GRAPH_REPORT.md or the analysis sidecar, so no report ever describes a graph that was not persisted (#1392). The message points at the escape hatch: an intentional shrink (you deleted files) requires a full rebuild with --force.
Source
Thrown at graphify/skill.md:437
raise SystemExit(1)
communities = cluster(G)
cohesion = score_all(G, communities)
tokens = {'input': extraction.get('input_tokens', 0), 'output': extraction.get('output_tokens', 0)}
gods = god_nodes(G)
surprises = surprising_connections(G, communities)
labels = {cid: 'Community ' + str(cid) for cid in communities}
# Placeholder questions - regenerated with real labels in Step 5
questions = suggest_questions(G, communities, labels)
# Export FIRST and honor the #479 shrink-guard: to_json returns False (writing
# nothing) when the new graph is smaller than the existing graph.json. Only write
# GRAPH_REPORT.md + the analysis sidecar when the graph was actually written, so
# they never describe a graph that graph.json doesn't contain (#1392).
wrote = to_json(G, communities, 'graphify-out/graph.json')
if not wrote:
print('ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).')
print('If this shrink is intentional (you deleted files), re-run a full build with --force.')
raise SystemExit(1)
report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, 'INPUT_PATH', suggested_questions=questions)
Path('graphify-out/GRAPH_REPORT.md').write_text(report, encoding=\"utf-8\")
analysis = {
'communities': {str(k): v for k, v in communities.items()},
'cohesion': {str(k): v for k, v in cohesion.items()},
'gods': gods,
'surprises': surprises,
'questions': questions,
}
Path('graphify-out/.graphify_analysis.json').write_text(json.dumps(analysis, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'Graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges, {len(communities)} communities')
"
```
If this step prints `ERROR: Graph is empty`, stop and tell the user what happened - do not proceed to labeling or visualization.
Replace INPUT_PATH with the actual path.
View on GitHub (pinned to 7fe58b0b0f)
Solutions
- If the shrink is intentional: re-run the full build with --force (or delete graphify-out/graph.json first) so the smaller graph can be written
- If it is NOT intentional: inspect .graphify_extract.json for missing files and re-run extraction — a node-count drop often means some files failed to extract
- Compare old vs new counts before deciding: load the existing graph.json node count and G.number_of_nodes() to see how many nodes vanished
- Check .graphifyignore for newly added patterns that silently exclude previously indexed sources
Example fix
# before $ graphify build # → ERROR: refused to shrink graphify-out/graph.json (#479) # after (shrink is intentional — files were deleted) $ graphify extract . --force # full rebuild overwrites the larger stale graph # or: rm graphify-out/graph.json && re-run the Step 4 block
Defensive patterns
Strategy: fallback
Validate before calling
import json
from pathlib import Path
new_count = G.number_of_nodes()
graph_path = Path('graphify-out/graph.json')
existing_count = (
len(json.loads(graph_path.read_text(encoding='utf-8')).get('nodes', []))
if graph_path.exists() else -1
)
if existing_count >= 0 and new_count < existing_count:
raise SystemExit(
f'shrink detected ({existing_count}→{new_count}); '
'pass --force or delete graph.json if intentional'
) Prevention
- After deleting many source files, run the full rebuild with --force instead of an incremental update
- When changing INPUT_PATH, clean graphify-out/ first so old and new graphs are never compared
- Treat a large unexplained node-count drop as an extraction failure: audit the extract JSON before forcing
When it happens
Trigger: Running the Step 4 build when the new extraction yields fewer nodes than the existing graph.json — e.g. you deleted or renamed many source files, .graphifyignore now excludes previously indexed paths, extraction partially failed for some files, or you are building against a smaller subdirectory over an old graphify-out/.
Common situations: Incremental --update after a big cleanup/refactor; switching INPUT_PATH to a smaller tree without clearing graphify-out/; an extraction run where a subset of files errored so their nodes are missing; duplicate-node dedup reducing the count.
Related errors
- module {__name__!r} has no attribute {name!r}
- Cannot read graph file {path}: {exc}. Re-run 'graphify extra
- cannot stat {p}: {exc}
- communities dict is empty — refusing to clear wiki/. Run `gr
- all community node IDs are stale — none exist in the graph.
AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14).
Data as JSON: /api/errors/b7d4e7bc80b31f53.
Report an issue: GitHub.