{"record":{"id":"ea2804615e957c49","repo":"666ghj/MiroFish","slug":"reconstruction-point-must-be-an-object","errorCode":null,"errorMessage":"reconstruction point must be an object","messagePattern":"reconstruction point must be an object","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":400,"sourceCode":"    if reconstruction_method not in {\n        \"current_stargazers_starred_at\",\n        \"aggregate_snapshot_only\",\n    }:\n        raise StarHistoryError(\"unsupported reconstruction method\")\n    generated_at = _parse_state_timestamp(\n        reconstruction[\"generated_at\"], \"reconstruction.generated_at\"\n    )\n    daily = reconstruction[\"daily\"]\n    if not isinstance(daily, list):\n        raise StarHistoryError(\"reconstruction.daily must be a list\")\n    if reconstruction_method == \"aggregate_snapshot_only\" and daily:\n        raise StarHistoryError(\"aggregate-only history cannot contain reconstructed dates\")\n\n    previous_day: date | None = None\n    previous_stars = 0\n    for index, raw_point in enumerate(daily):\n        if not isinstance(raw_point, dict):\n            raise StarHistoryError(\"reconstruction point must be an object\")\n        _expect_keys(raw_point, {\"date\", \"stars\"}, \"reconstruction point\")\n        raw_date = raw_point[\"date\"]\n        if not isinstance(raw_date, str):\n            raise StarHistoryError(\"reconstruction date must be a string\")\n        try:\n            point_day = date.fromisoformat(raw_date)\n        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():","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L382-L418","documentation":"Raised by validate_state() while iterating reconstruction.daily when an element is not a JSON object. Each daily point must be a dict with exactly the keys 'date' and 'stars' (enforced next by _expect_keys); arrays, strings, numbers, or nulls fail here.","triggerScenarios":"validate_state(state) where a daily entry is e.g. [\"2024-01-01\", 10] (tuple-style array) or a bare integer star count.","commonSituations":"Producing state from a different language/serializer that emitted arrays instead of objects; hand-editing the daily list; a partially applied data transform.","solutions":["Make every daily element an object {\"date\": ..., \"stars\": ...} with exactly those two keys","Remove any extra fields — _expect_keys rejects unknown as well as missing fields","Regenerate the state with the canonical writer (canonical_state_bytes)"],"exampleFix":"// before\n\"daily\": [[\"2024-01-01\", 10]]\n// after\n\"daily\": [{\"date\": \"2024-01-01\", \"stars\": 10}]","handlingStrategy":"type-guard","validationCode":"daily = state[\"reconstruction\"][\"daily\"]\nif not all(isinstance(p, dict) for p in daily):\n    daily = [{\"date\": p[0], \"stars\": p[1]} if isinstance(p, (list, tuple)) else p for p in daily]","typeGuard":"def are_daily_points_objects(daily: list) -> bool:\n    return all(isinstance(p, dict) and set(p) == {\"date\", \"stars\"} for p in daily)","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"reconstruction point must be an object\" in str(exc):\n        # locate the offending index via enumerate and re-serialize that point\n        ...","preventionTips":["Use json.dumps on dicts, not on tuples, when producing state","Validate producer output with validate_state before persisting","Keep daily elements structurally uniform"],"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"}