{"record":{"id":"8409a264b976e2f3","repo":"666ghj/MiroFish","slug":"history-state-must-be-a-json-object","errorCode":null,"errorMessage":"history state must be a JSON object","messagePattern":"history state must be a JSON object","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":346,"sourceCode":"    normalized = _normalize_now(value)\n    return normalized.strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n\n\ndef _normalize_now(value: datetime) -> datetime:\n    if value.tzinfo is None or value.utcoffset() != timedelta(0):\n        raise StarHistoryError(\"clock must return a UTC datetime\")\n    return value.astimezone(UTC).replace(microsecond=0)\n\n\ndef _expect_keys(value: Mapping[str, Any], expected: set[str], label: str) -> None:\n    actual = set(value)\n    if actual != expected:\n        raise StarHistoryError(f\"{label} contains missing or unknown fields\")\n\n\ndef validate_state(state: Any) -> None:\n    if not isinstance(state, dict):\n        raise StarHistoryError(\"history state must be a JSON object\")\n    _expect_keys(\n        state,\n        {\n            \"schema_version\",\n            \"repository\",\n            \"timezone\",\n            \"ongoing_interval_days\",\n            \"reconstruction\",\n            \"snapshots\",\n        },\n        \"history state\",\n    )\n    if state[\"schema_version\"] != 1 or type(state[\"schema_version\"]) is not int:\n        raise StarHistoryError(\"unsupported history schema_version\")\n    if state[\"repository\"] != REPOSITORY:\n        raise StarHistoryError(\"history repository does not match configured repository\")\n    if state[\"timezone\"] != \"UTC\":\n        raise StarHistoryError(\"history timezone must be UTC\")","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L328-L364","documentation":"validate_state requires the loaded history state to be a JSON object at the top level; anything else (list, string, number, null, bool) raises StarHistoryError('history state must be a JSON object'). This fires before _expect_keys so callers get a clear message rather than a KeyError on dict access.","triggerScenarios":"A state file containing '[...]' (a JSON array, e.g. an old snapshots-only format), a bare string, or 'null'; also a truncated file that happens to decode to a scalar.","commonSituations":"State format changed across script versions (array -> object); the file was overwritten by another tool's output; a failed atomic write left partial JSON.","solutions":["Inspect the first bytes of the state file to see what shape it actually is","Regenerate the state file via the script's normal run path (full reconstruction from GitHub)","If migrating from an old array format, write a one-off migration or discard and rebuild"],"exampleFix":"// before (state file)\n[ {\"date\": \"2024-01-01\", \"stars\": 10} ]\n\n// after (state file)\n{ \"schema_version\": 1, \"repository\": \"666ghj/MiroFish\", \"timezone\": \"UTC\", \"ongoing_interval_days\": 13, \"reconstruction\": {\"method\": \"...\", \"generated_at\": \"2024-01-15T10:30:00Z\", \"daily\": []}, \"snapshots\": [] }","handlingStrategy":"type-guard","validationCode":"import json\nwith state_path.open() as fh:\n    state = json.load(fh)\nif not isinstance(state, dict):\n    raise StarHistoryError(f\"state file is a {type(state).__name__}, expected a JSON object\")","typeGuard":"def is_state_object(value: object) -> TypeGuard[dict]:\n    return isinstance(value, dict)","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"must be a JSON object\" in str(exc):\n        regenerate_state_file()  # full reconstruction from GitHub\n    else:\n        raise","preventionTips":["Write state files atomically (temp file + rename) so truncation cannot leave partial JSON","Version your state format with schema_version and migrate forward instead of keeping legacy shapes"],"tags":["state-file","validation","json","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}