From b890ca659011587ecbe22c8ed1c83571b2a85b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=9E=AC=ED=98=84?= <231584193+kimdzhekhon@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:15:52 +0900 Subject: [PATCH] fix(serve): reorder except clauses so the corrupted-graph message is reachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit json.JSONDecodeError subclasses ValueError, so the broader `except (ValueError, FileNotFoundError)` clause always matched first, making the intended "graph.json is corrupted (...). Re-run /graphify to rebuild." recovery hint dead code — users with a truncated graph.json got the bare json.JSONDecodeError message instead, contradicting the behavior SECURITY.md documents for this exact threat. Move the json.JSONDecodeError clause first so it actually catches. Audited the rest of serve.py's exception handlers for the same narrower-after-broader ordering bug; found no other instance. Fixes #2005. --- graphify/serve.py | 6 +++--- tests/test_serve.py | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/graphify/serve.py b/graphify/serve.py index 0a0e7bfe..457171e5 100644 --- a/graphify/serve.py +++ b/graphify/serve.py @@ -55,12 +55,12 @@ def _load_graph(graph_path: str) -> nx.Graph: except Exception: G.graph["_learning_overlay"] = {} return G - except (ValueError, FileNotFoundError) as exc: - print(f"error: {exc}", file=sys.stderr) - sys.exit(1) except json.JSONDecodeError as exc: print(f"error: graph.json is corrupted ({exc}). Re-run /graphify to rebuild.", file=sys.stderr) sys.exit(1) + except (ValueError, FileNotFoundError) as exc: + print(f"error: {exc}", file=sys.stderr) + sys.exit(1) def _communities_from_graph(G: nx.Graph) -> dict[int, list[str]]: diff --git a/tests/test_serve.py b/tests/test_serve.py index 30278718..bf0b4f4b 100644 --- a/tests/test_serve.py +++ b/tests/test_serve.py @@ -599,6 +599,19 @@ def test_load_graph_missing_file(tmp_path): _load_graph(str(graphify_dir / "nonexistent.json")) +def test_load_graph_corrupted_json_prints_recovery_message(tmp_path, capsys): + """json.JSONDecodeError is a ValueError subclass, so its except clause + must be checked before the bare (ValueError, FileNotFoundError) clause, + or the corrupted-graph recovery hint is unreachable (#2005).""" + p = tmp_path / "graph.json" + p.write_text("{not valid json") + with pytest.raises(SystemExit): + _load_graph(str(p)) + err = capsys.readouterr().err + assert "graph.json is corrupted" in err + assert "Re-run /graphify to rebuild" in err + + def test_load_graph_rejects_oversized_file(monkeypatch, tmp_path, capsys): # #F4: oversized graph.json must fail fast (SystemExit) with a clear error. G = _make_graph()