{"record":{"id":"c60faf3f0355507b","repo":"Graphify-Labs/graphify","slug":"error-graph-file-must-contain-a-json-object-pat","errorCode":null,"errorMessage":"ERROR: graph file must contain a JSON object: {path}","messagePattern":"ERROR: graph file must contain a JSON object: (.+?)","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"graphify/callflow_html.py","lineNumber":275,"sourceCode":"        edge = dict(attrs)\n        edge[\"source\"] = edge.get(\"_src\", edge.get(\"source\", source))\n        edge[\"target\"] = edge.get(\"_tgt\", edge.get(\"target\", target))\n        edge.setdefault(\"id\", f\"edge_{index}\")\n        edges.append(edge)\n    return nodes, edges\n\n\ndef load_graph(path: str | Path) -> tuple:\n    \"\"\"Load graph.json. Returns normalized (nodes, edges, hyperedges, metadata).\"\"\"\n    if path:\n        from graphify.security import check_graph_file_size_cap\n        try:\n            check_graph_file_size_cap(Path(path))\n        except ValueError as exc:\n            raise SystemExit(f\"ERROR: {exc}\") from exc\n    data = read_json(path)\n    if not isinstance(data, dict):\n        raise SystemExit(f\"ERROR: graph file must contain a JSON object: {path}\")\n\n    graph_block = data.get(\"graph\") if isinstance(data.get(\"graph\"), dict) else {}\n    meta_block = data.get(\"metadata\") if isinstance(data.get(\"metadata\"), dict) else {}\n\n    node_link = _node_link_payload(data)\n    if node_link:\n        raw_nodes, raw_edges = node_link\n    else:\n        raw_nodes = first_list(data.get(\"nodes\"), data.get(\"vertices\"), graph_block.get(\"nodes\"), graph_block.get(\"vertices\"))\n        raw_edges = first_list(data.get(\"links\"), data.get(\"edges\"), graph_block.get(\"links\"), graph_block.get(\"edges\"))\n    hyperedges = first_list(data.get(\"hyperedges\"), graph_block.get(\"hyperedges\"), data.get(\"groups\"), graph_block.get(\"groups\"))\n\n    nodes = [normalize_node(n, i) for i, n in enumerate(raw_nodes) if isinstance(n, dict)]\n    edges = []\n    for i, raw_edge in enumerate(raw_edges):\n        if not isinstance(raw_edge, dict):\n            continue\n        edge = normalize_edge(raw_edge, i)","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/Graphify-Labs/graphify/blob/7fe58b0b0f3873be9a21c30106b8b8527c353aa6/graphify/callflow_html.py#L257-L293","documentation":"Empty-graph guard in the aider-flavored fragment (tools/skillgen/fragments/core/aider.md:412). Unlike the newer core fragment, this variant reads .graphify_extract.json from the CURRENT directory (Path('.graphify_extract.json'), no graphify-out/ prefix) and calls build_from_json(extraction, directed=IS_DIRECTED) without root=. Zero nodes abort with SystemExit(1) before any write (#1392).","triggerScenarios":"build_from_json() on the extraction JSON yields 0 nodes: empty nodes array after extraction, all files skipped, or the extraction stage failed. Additionally, because sidecars are read from cwd rather than graphify-out/, running from a directory that contains a stale/empty .graphify_extract.json triggers it even when a good extraction exists elsewhere.","commonSituations":"Running the aider workflow from a subdirectory so .graphify_extract.json resolves to the wrong file; legacy layout where sidecars lived at repo root; binary-only or fully-skipped corpus; silent upstream extraction failure.","solutions":["Check that .graphify_extract.json in the CURRENT directory has a non-empty nodes array; if it is stale or from another run, delete it.","Run the pipeline from the directory where extraction actually wrote its sidecars, or update to the newer fragment that reads graphify-out/ prefixed paths.","Re-run extraction on a corpus with extractable text files; inspect .graphify_detect.json for skip reasons.","Verify the extraction stage completed successfully (token counts, logs) before the build step."],"exampleFix":"# before: cwd-sidecar path resolves to an empty legacy file\nPath('.graphify_extract.json')  # empty -> 0 nodes\n\n# after: read from the canonical output dir\nextraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding='utf-8'))","handlingStrategy":"validation","validationCode":"import json\nfrom pathlib import Path\n\n# this legacy fragment reads cwd-relative sidecars - check BOTH locations\nfor p in (Path('.graphify_extract.json'), Path('graphify-out/.graphify_extract.json')):\n    if p.exists():\n        nodes = json.loads(p.read_text()).get('nodes') or []\n        print(f'{p}: {len(nodes)} nodes')\n        if not nodes:\n            raise SystemExit(f'{p} is empty - delete it or re-run extraction')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run the aider pipeline from the directory where extraction wrote its sidecars, or migrate to the graphify-out/-prefixed fragment.","Delete legacy cwd-level .graphify_extract.json files so they cannot shadow fresh extractions.","Verify extraction token counts/logs before the build step.","Precheck that the nodes list is non-empty."],"tags":["graphify","aider","empty-graph","cwd","extraction"],"backgroundTag":null,"analyzedSha":"7fe58b0b0f3873be9a21c30106b8b8527c353aa6","analyzedAt":"2026-08-14T19:23:21.323Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}