{"record":{"id":"e30a712f18503d4a","repo":"666ghj/MiroFish","slug":"snapshot-must-be-an-object","errorCode":null,"errorMessage":"snapshot must be an object","messagePattern":"snapshot must be an object","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":430,"sourceCode":"        if index == 0 and stars <= 0:\n            raise StarHistoryError(\"first reconstruction point must have stars\")\n        if previous_day is not None and point_day <= previous_day:\n            raise StarHistoryError(\"reconstruction dates must be strictly increasing\")\n        if index > 0 and stars <= previous_stars:\n            raise StarHistoryError(\"reconstruction stars must be strictly increasing\")\n        if point_day >= generated_at.date():\n            raise StarHistoryError(\"reconstruction must contain only completed UTC dates\")\n        previous_day = point_day\n        previous_stars = stars\n\n    snapshots = state[\"snapshots\"]\n    if not isinstance(snapshots, list):\n        raise StarHistoryError(\"snapshots must be a list\")\n    previous_snapshot: datetime | None = None\n    first_snapshot: datetime | None = None\n    for raw_snapshot in snapshots:\n        if not isinstance(raw_snapshot, dict):\n            raise StarHistoryError(\"snapshot must be an object\")\n        _expect_keys(raw_snapshot, {\"at\", \"stars\"}, \"snapshot\")\n        snapshot_at = _parse_state_timestamp(raw_snapshot[\"at\"], \"snapshot.at\")\n        _strict_non_negative_int(raw_snapshot[\"stars\"], \"snapshot stars\")\n        if previous_snapshot is not None and snapshot_at <= previous_snapshot:\n            raise StarHistoryError(\"snapshot timestamps must be strictly increasing\")\n        if first_snapshot is None:\n            first_snapshot = snapshot_at\n        previous_snapshot = snapshot_at\n\n    if first_snapshot is not None:\n        if first_snapshot < generated_at:\n            raise StarHistoryError(\"first snapshot cannot predate reconstruction\")\n        if previous_day is not None and previous_day >= first_snapshot.date():\n            raise StarHistoryError(\"reconstruction dates must predate snapshots\")\n\n\ndef canonical_state_bytes(state: Mapping[str, Any]) -> bytes:\n    validate_state(state)","sourceCodeStart":412,"sourceCodeEnd":448,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L412-L448","documentation":"Raised by validate_state() while iterating snapshots when an element is not a JSON object. Each snapshot must be a dict with exactly the keys 'at' (UTC timestamp string) and 'stars' (non-negative int), checked immediately after this guard.","triggerScenarios":"validate_state(state) where a snapshot element is an array like [\"2024-01-02T00:00:00Z\", 300] or a bare integer.","commonSituations":"Serializing snapshots from tuple records; cross-language producers emitting arrays; hand-edited state files.","solutions":["Emit each snapshot as an object with exactly 'at' and 'stars'","Remove extra fields — the exact-key check rejects unknown fields too","Regenerate the state file rather than patching entries"],"exampleFix":"// before\n\"snapshots\": [[\"2024-01-02T00:00:00Z\", 300]]\n// after\n\"snapshots\": [{\"at\": \"2024-01-02T00:00:00Z\", \"stars\": 300}]","handlingStrategy":"type-guard","validationCode":"snaps = [\n    {\"at\": s[0], \"stars\": s[1]} if isinstance(s, (list, tuple)) else s\n    for s in snaps\n]","typeGuard":"def are_snapshot_objects(snaps: list) -> bool:\n    return all(isinstance(s, dict) and set(s) == {\"at\", \"stars\"} for s in snaps)","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"snapshot must be an object\" in str(exc):\n        # re-serialize tuple-form snapshots as objects, then re-validate\n        ...","preventionTips":["Emit dict records, not tuples, from snapshot producers","Validate with the exact-key rule in mind: no extra fields","Regenerate rather than hand-patch malformed snapshots"],"tags":["validation","json","state-file"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}