{"record":{"id":"49db4f5000baacb3","repo":"666ghj/MiroFish","slug":"snapshots-must-be-a-list","errorCode":null,"errorMessage":"snapshots must be a list","messagePattern":"snapshots must be a list","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":425,"sourceCode":"        except ValueError as exc:\n            raise StarHistoryError(\"reconstruction date is invalid\") from exc\n        if point_day.isoformat() != raw_date:\n            raise StarHistoryError(\"reconstruction date is not canonical\")\n        stars = _strict_non_negative_int(raw_point[\"stars\"], \"reconstruction stars\")\n        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():","sourceCodeStart":407,"sourceCodeEnd":443,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L407-L443","documentation":"Raised by validate_state() when the top-level 'snapshots' field of the state is not a JSON array. Snapshots are ongoing interval measurements (objects with 'at' and 'stars') appended over time; a non-list value fails before any snapshot is examined.","triggerScenarios":"validate_state(state) where state['snapshots'] is a dict, string, number, or null — e.g. keyed by timestamp instead of a list.","commonSituations":"Hand-editing or restructuring state; consuming state from a producer version with a different snapshots shape; JSON merge tools changing arrays to objects.","solutions":["Make snapshots a JSON array of {\"at\": \"YYYY-MM-DDTHH:MM:SSZ\", \"stars\": <int>} objects (an empty array is valid)","If snapshots were keyed by timestamp, convert to a list sorted by 'at'","Regenerate state with the canonical writer"],"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 = state.get(\"snapshots\")\nif not isinstance(snaps, list):\n    state[\"snapshots\"] = sorted(\n        [{\"at\": at, \"stars\": s} for at, s in snaps.items()],\n        key=lambda s: s[\"at\"],\n    ) if isinstance(snaps, dict) else []","typeGuard":"def is_snapshots_list(value) -> bool:\n    return isinstance(value, list) and all(\n        isinstance(s, dict) and set(s) == {\"at\", \"stars\"} for s in value\n    )","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"snapshots must be a list\" in str(exc):\n        # normalize to a list of {at, stars} objects, then re-validate\n        ...","preventionTips":["Serialize snapshots as an array of objects","Append new snapshots rather than rewriting the structure","Validate state in CI when it is committed"],"tags":["validation","json","state-file"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}