Handle malformed/missing builds.json gracefully in refresh & validate_json#24
Merged
Conversation
…_json Two scripts could crash with an unhandled exception (and a Python traceback) instead of the project's clean common.die()/exit-1 path when builds.json is malformed or missing — e.g. after a merge conflict or a hand-edit during a release. refresh.py: builds.load() / find_cli() sat one line above the try block whose `except ValueError` / `except OSError` handlers were clearly meant to cover them (JSONDecodeError is a ValueError subclass; FileNotFoundError an OSError). Move both calls inside the try so a bad builds.json is reported via common.die() instead of escaping as a raw traceback. validate_json.py: main() re-parsed builds.json/builds.schema.json with no guard. check_sorted_keys() catches JSONDecodeError but only logs and continues, so execution still reached the unguarded re-parse and crashed — ironic for the very tool meant to validate that file. Guard the parse and return 1 with a clean error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves operator-facing failure behavior in the release automation scripts by ensuring missing/malformed builds.json (and schema) errors follow the project’s clean common.err() / exit-1 path instead of producing uncaught Python tracebacks.
Changes:
scripts/refresh.py: movesbuilds.load()/builds.find_cli()inside the existingtryso malformed/missingbuilds.jsonis handled by existingexceptblocks.scripts/validate_json.py: wraps parsing ofbuilds.json/builds.schema.jsonin a guard to return1with a clean error message on read/parse failure.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| scripts/validate_json.py | Adds guarded load/parse of builds.json and builds.schema.json to avoid unhandled exceptions. |
| scripts/refresh.py | Ensures builds.json load/lookup happens inside existing error-handling try block. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Path.read_text() raises UnicodeDecodeError on a non-UTF-8 file, which is a ValueError subclass and so was not caught by the (OSError, json.JSONDecodeError) guard — it would still crash with a traceback. Add it to the guard so a non-UTF-8 builds.json also exits 1 cleanly. (refresh.py is already covered: its handler is the broader `except ValueError`, of which UnicodeDecodeError is a subclass.) Addresses Copilot review feedback on PR #24. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fnando
enabled auto-merge (squash)
June 10, 2026 00:02
fnando
approved these changes
Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two release-automation scripts could crash with an unhandled exception and a Python traceback instead of the project's clean
common.die()/ exit-1 path whenbuilds.json(orbuilds.schema.json) is malformed or missing — the kind of state you get from a merge conflict or a hand-edit during a release. Both are small, mechanical fixes that route the failure into the error handling the code already intends.These surfaced from a code review of the repo. Neither can corrupt a published image or pass a check that should fail — the impact is purely operator-facing log hygiene (traceback vs. clean error) — but
validate_jsonin particular is the tool meant to catch a badbuilds.json, so crashing on one is a poor failure mode.Bug 1 —
refresh.py:builds.load()outside itstryblockmain()calledbuilds.load()/find_cli()one line above thetrywhoseexcept ValueError/except OSErrorhandlers were clearly meant to cover them —json.JSONDecodeErroris a subclass ofValueError, andFileNotFoundErrora subclass ofOSError. Because the call sat outside the block, a malformedbuilds.jsonraised an uncaughtJSONDecodeErrorinstead of being reported viacommon.die().Fix: move
builds.load()/find_cli()inside the existingtry. No new handler needed — the existingexcept ValueError/except OSErrornow cover it.Bug 2 —
validate_json.py: unguarded re-parse inmain()main()re-parsedbuilds.jsonandbuilds.schema.jsonwith no guard.check_sorted_keys()does catchJSONDecodeError, but only logs and continues (it doesn't stop the run), so execution still reached the unguarded re-parse on the same file and crashed with a traceback.Fix: guard the parse and return
1with a clean error message, consistent with the rest of the script.Testing
tests/integration/test_refresh.py— 12 passed.builds.json:refresh.py→SystemExit(1)viacommon.die()(clean stderr message), not a rawJSONDecodeError.validate_json.py→ guarded block catchesFileNotFoundErrorandJSONDecodeError, returns1.🤖 Generated with Claude Code