{"record":{"id":"b7d4e7bc80b31f53","repo":"Graphify-Labs/graphify","slug":"error-refused-to-shrink-graphify-out-graph-json","errorCode":null,"errorMessage":"ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).","messagePattern":"ERROR: refused to shrink graphify-out/graph\\.json \\(existing graph has more nodes; #479\\)\\.","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"graphify/skill.md","lineNumber":437,"sourceCode":"    raise SystemExit(1)\ncommunities = cluster(G)\ncohesion = score_all(G, communities)\ntokens = {'input': extraction.get('input_tokens', 0), 'output': extraction.get('output_tokens', 0)}\ngods = god_nodes(G)\nsurprises = surprising_connections(G, communities)\nlabels = {cid: 'Community ' + str(cid) for cid in communities}\n# Placeholder questions - regenerated with real labels in Step 5\nquestions = suggest_questions(G, communities, labels)\n\n# Export FIRST and honor the #479 shrink-guard: to_json returns False (writing\n# nothing) when the new graph is smaller than the existing graph.json. Only write\n# GRAPH_REPORT.md + the analysis sidecar when the graph was actually written, so\n# they never describe a graph that graph.json doesn't contain (#1392).\nwrote = to_json(G, communities, 'graphify-out/graph.json')\nif not wrote:\n    print('ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).')\n    print('If this shrink is intentional (you deleted files), re-run a full build with --force.')\n    raise SystemExit(1)\nreport = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, 'INPUT_PATH', suggested_questions=questions)\nPath('graphify-out/GRAPH_REPORT.md').write_text(report, encoding=\\\"utf-8\\\")\nanalysis = {\n    'communities': {str(k): v for k, v in communities.items()},\n    'cohesion': {str(k): v for k, v in cohesion.items()},\n    'gods': gods,\n    'surprises': surprises,\n    'questions': questions,\n}\nPath('graphify-out/.graphify_analysis.json').write_text(json.dumps(analysis, indent=2, ensure_ascii=False), encoding=\\\"utf-8\\\")\nprint(f'Graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges, {len(communities)} communities')\n\"\n```\n\nIf this step prints `ERROR: Graph is empty`, stop and tell the user what happened - do not proceed to labeling or visualization.\n\nReplace INPUT_PATH with the actual path.\n","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/skill.md#L419-L455","documentation":"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.","triggerScenarios":"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/.","commonSituations":"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.","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"],"exampleFix":"# before\n$ graphify build   # → ERROR: refused to shrink graphify-out/graph.json (#479)\n\n# after (shrink is intentional — files were deleted)\n$ graphify extract . --force   # full rebuild overwrites the larger stale graph\n# or: rm graphify-out/graph.json && re-run the Step 4 block","handlingStrategy":"fallback","validationCode":"import json\nfrom pathlib import Path\n\nnew_count = G.number_of_nodes()\ngraph_path = Path('graphify-out/graph.json')\nexisting_count = (\n    len(json.loads(graph_path.read_text(encoding='utf-8')).get('nodes', []))\n    if graph_path.exists() else -1\n)\nif existing_count >= 0 and new_count < existing_count:\n    raise SystemExit(\n        f'shrink detected ({existing_count}→{new_count}); '\n        'pass --force or delete graph.json if intentional'\n    )","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["graphify","data-integrity","shrink-guard","refuse-to-degrade","python"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}