From 7998474534e5f06e62027d3a445b6839efef41c5 Mon Sep 17 00:00:00 2001 From: Jer Miller Date: Wed, 29 Jul 2026 22:57:18 -0600 Subject: [PATCH] fix(maint): keep invalid-UTF-8 schedules.json out of the 009 retry loop json.loads decodes bytes before parsing, so an invalid-UTF-8 config/schedules.json raised UnicodeDecodeError rather than JSONDecodeError and escaped run_migration uncaught. With MAINT_RETRY_ON_NEXT_START the task would then re-fail on every supervisor start, appending an attempt block to its state file each boot. Catch UnicodeDecodeError alongside JSONDecodeError on the read-side parse so it maps to the same "unparseable" skip reason and exits 0. Caught as an explicit tuple, not ValueError, which would over-catch MalformedDataError and steal its write-side error disposition. Co-Authored-By: Claude Opus 5 (1M context) --- solstone/apps/sol/maint/009_remove_granola_sync_schedule.py | 2 +- tests/test_maint_009_remove_granola_sync_schedule.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/solstone/apps/sol/maint/009_remove_granola_sync_schedule.py b/solstone/apps/sol/maint/009_remove_granola_sync_schedule.py index 982163683..a3b140666 100644 --- a/solstone/apps/sol/maint/009_remove_granola_sync_schedule.py +++ b/solstone/apps/sol/maint/009_remove_granola_sync_schedule.py @@ -85,7 +85,7 @@ def run_migration(*, dry_run: bool) -> MigrationSummary: try: raw = json.loads(raw_bytes) - except json.JSONDecodeError: + except (UnicodeDecodeError, json.JSONDecodeError): summary.skipped_reason = "unparseable" return summary diff --git a/tests/test_maint_009_remove_granola_sync_schedule.py b/tests/test_maint_009_remove_granola_sync_schedule.py index efdc3e910..307713e63 100644 --- a/tests/test_maint_009_remove_granola_sync_schedule.py +++ b/tests/test_maint_009_remove_granola_sync_schedule.py @@ -250,6 +250,7 @@ def test_second_run_is_noop_and_does_not_rewrite_schedules_file(tmp_path): pytest.param(None, "no file", id="missing-file"), pytest.param(b"", "empty file", id="empty-file"), pytest.param(b"{not json", "unparseable", id="non-json"), + pytest.param(b"\xff\xfe\x00", "unparseable", id="invalid-utf8"), pytest.param(b"[]", "unparseable", id="non-dict"), ], ) -- 2.51.2