Summary
During a triage pass, the save-loading paths were read end-to-end. The load handlers are clearly meant to recover gracefully from a bad save (they print a warning and create a fresh object), but they only catch (IOError, OSError, json.JSONDecodeError) — not KeyError. The reader/writers access most fields with direct [] indexing, so a save file that is valid JSON but missing a field raises an uncaught KeyError and crashes the game instead of recovering.
src/fishE.py:227-234 (loadPlayer) — except (IOError, OSError, json.JSONDecodeError) as e: does not include KeyError. Same pattern in loadStats (src/fishE.py:236) and loadTimeService (src/fishE.py:245).
src/player/playerJsonReaderWriter.py:18-22 — direct access: player.fishCount = playerJson["fishCount"], ... = playerJson["money"], etc. (only energy uses .get(..., 100) at line 23).
src/stats/statsJsonReaderWriter.py:19-24 — direct access for six fields (only moneyLostWhileDrunk uses .get(..., 0) at line 26).
src/world/timeServiceJsonReaderWriter.py:11-12 — timeService.time = timeServiceJson["time"], ... = timeServiceJson["day"] (no defaults).
Why it matters
A truncated, hand-edited, or older-format save (anything missing a key) crashes on startup rather than falling back to a new game, which is exactly the failure the surrounding try/except was written to prevent. It also undermines the project's backward-compatibility approach to save files — the .get(default) pattern was added for energy/moneyLostWhileDrunk precisely so old saves keep loading, but the remaining fields don't follow it.
Suggested fix
Make field loading backward-compatible and crash-safe. Either (a) read every persisted field with dict.get("<field>", <default>) consistently across the three reader/writers, or (b) add KeyError (and ValueError) to the caught exception tuples in loadPlayer/loadStats/loadTimeService. Option (a) is preferable since it preserves partial data instead of discarding the whole object. Add a regression test that loads a save dict missing a field and asserts graceful handling.
Filed by Claude during an automated triage pass; claims above were verified against source.
Summary
During a triage pass, the save-loading paths were read end-to-end. The load handlers are clearly meant to recover gracefully from a bad save (they print a warning and create a fresh object), but they only catch
(IOError, OSError, json.JSONDecodeError)— notKeyError. The reader/writers access most fields with direct[]indexing, so a save file that is valid JSON but missing a field raises an uncaughtKeyErrorand crashes the game instead of recovering.src/fishE.py:227-234(loadPlayer) —except (IOError, OSError, json.JSONDecodeError) as e:does not includeKeyError. Same pattern inloadStats(src/fishE.py:236) andloadTimeService(src/fishE.py:245).src/player/playerJsonReaderWriter.py:18-22— direct access:player.fishCount = playerJson["fishCount"],... = playerJson["money"], etc. (onlyenergyuses.get(..., 100)at line 23).src/stats/statsJsonReaderWriter.py:19-24— direct access for six fields (onlymoneyLostWhileDrunkuses.get(..., 0)at line 26).src/world/timeServiceJsonReaderWriter.py:11-12—timeService.time = timeServiceJson["time"],... = timeServiceJson["day"](no defaults).Why it matters
A truncated, hand-edited, or older-format save (anything missing a key) crashes on startup rather than falling back to a new game, which is exactly the failure the surrounding
try/exceptwas written to prevent. It also undermines the project's backward-compatibility approach to save files — the.get(default)pattern was added forenergy/moneyLostWhileDrunkprecisely so old saves keep loading, but the remaining fields don't follow it.Suggested fix
Make field loading backward-compatible and crash-safe. Either (a) read every persisted field with
dict.get("<field>", <default>)consistently across the three reader/writers, or (b) addKeyError(andValueError) to the caught exception tuples inloadPlayer/loadStats/loadTimeService. Option (a) is preferable since it preserves partial data instead of discarding the whole object. Add a regression test that loads a save dict missing a field and asserts graceful handling.Filed by Claude during an automated triage pass; claims above were verified against source.