fix(serve): reorder except clauses so the corrupted-graph message is reachable#2017
fix(serve): reorder except clauses so the corrupted-graph message is reachable#2017kimdzhekhon wants to merge 2 commits into
Conversation
…reachable 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 Graphify-Labs#2005.
Job28703
left a comment
There was a problem hiding this comment.
Independently reproduced the fail-then-pass: checked out v8 base (unfixed) with the new test applied — test_load_graph_corrupted_json_prints_recovery_message fails exactly as expected (bare JSONDecodeError message instead of the recovery hint), confirming json.JSONDecodeError is indeed swallowed by the broader (ValueError, FileNotFoundError) clause ahead of it. With the reorder applied, the same test passes (6 passed on test_serve.py -k load_graph). Also spot-checked the rest of serve.py's ~20 except clauses — no other narrower-after-broader ordering issue. Clean, well-scoped fix. Thanks!
HerenderKumar
left a comment
There was a problem hiding this comment.
Came here from #2005 — I had claimed it on the issue a few hours before this PR went up and had the same fix built locally, so I can vouch for the approach: json.JSONDecodeError subclasses ValueError, so moving its clause first is exactly right. I ran the same change against the full serve suites locally (117 passed, 1 skipped).
One suggestion: a small guard test asserting that a non-decode ValueError (e.g. a non-.json path) still prints the generic error: ... message rather than the corrupted-graph hint — it pins the clause order so a future refactor can't "simplify" it back:
def test_load_graph_generic_value_error_message_unchanged(tmp_path, capsys):
p = tmp_path / "graph.txt"
p.write_text("not a graph")
with pytest.raises(SystemExit):
_load_graph(str(p))
err = capsys.readouterr().err
assert "must be a .json file" in err
assert "corrupted" not in errLGTM either way.
…ession Per review on Graphify-Labs#2017 (thanks @HerenderKumar): add a guard asserting a non-decode ValueError (e.g. a non-.json path) still prints the plain "must be a .json file" message, not the corrupted-graph hint. Locks in the except-clause order from the parent fix so a future refactor can't silently collapse the two branches back together.
|
Good catch — added that exact guard test. Thanks for vouching for the approach and for validating against the full serve suite. |
Summary
json.JSONDecodeErrorsubclassesValueError, so_load_graph()'sexcept (ValueError, FileNotFoundError)clause always matched first — theexcept json.JSONDecodeErrorclause with the "graph.json is corrupted (...). Re-run /graphify to rebuild." recovery message was unreachable dead code.graph.jsongot the bare underlyingjson.JSONDecodeErrormessage instead, contradicting whatSECURITY.mddocuments for this exact threat.json.JSONDecodeErrorclause first so it actually catches. Audited the rest ofserve.py's exception handlers for the same narrower-after-broader ordering bug — found no other instance.Fixes #2005.
Test plan
test_load_graph_corrupted_json_prints_recovery_message— writes invalid JSON, asserts the recovery message (not the bare decode error) is printeduv run pytest tests/test_serve.py -q -k load_graph— 6 passeduv run pytest tests/ -q— full suite: 3412 passed (5 pre-existing failures unrelated to this change, missing optionalopenaidependency in this environment)