666ghj/MiroFish · error · StarHistoryError

reconstruction date must be a string

Error message

reconstruction date must be a string

What it means

Raised by validate_state() when a daily point's 'date' field is not a Python str (JSON string). The date must be an ISO-format string that is subsequently parsed with date.fromisoformat, so non-string types fail before parsing is attempted.

Source

Thrown at scripts/star_history.py:404

        raise StarHistoryError("unsupported reconstruction method")
    generated_at = _parse_state_timestamp(
        reconstruction["generated_at"], "reconstruction.generated_at"
    )
    daily = reconstruction["daily"]
    if not isinstance(daily, list):
        raise StarHistoryError("reconstruction.daily must be a list")
    if reconstruction_method == "aggregate_snapshot_only" and daily:
        raise StarHistoryError("aggregate-only history cannot contain reconstructed dates")

    previous_day: date | None = None
    previous_stars = 0
    for index, raw_point in enumerate(daily):
        if not isinstance(raw_point, dict):
            raise StarHistoryError("reconstruction point must be an object")
        _expect_keys(raw_point, {"date", "stars"}, "reconstruction point")
        raw_date = raw_point["date"]
        if not isinstance(raw_date, str):
            raise StarHistoryError("reconstruction date must be a string")
        try:
            point_day = date.fromisoformat(raw_date)
        except ValueError as exc:
            raise StarHistoryError("reconstruction date is invalid") from exc
        if point_day.isoformat() != raw_date:
            raise StarHistoryError("reconstruction date is not canonical")
        stars = _strict_non_negative_int(raw_point["stars"], "reconstruction stars")
        if index == 0 and stars <= 0:
            raise StarHistoryError("first reconstruction point must have stars")
        if previous_day is not None and point_day <= previous_day:
            raise StarHistoryError("reconstruction dates must be strictly increasing")
        if index > 0 and stars <= previous_stars:
            raise StarHistoryError("reconstruction stars must be strictly increasing")
        if point_day >= generated_at.date():
            raise StarHistoryError("reconstruction must contain only completed UTC dates")
        previous_day = point_day
        previous_stars = stars

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Emit dates as strings in 'YYYY-MM-DD' form
  2. When generating state in Python, use point_day.isoformat() rather than str/int conversions
  3. Regenerate state rather than patching individual fields by hand

Example fix

// before
{"date": 20240101, "stars": 10}
// after
{"date": "2024-01-01", "stars": 10}
Defensive patterns

Strategy: type-guard

Validate before calling

for p in state["reconstruction"]["daily"]:
    if not isinstance(p["date"], str):
        p["date"] = p["date"].isoformat() if hasattr(p["date"], "isoformat") else str(p["date"])

Type guard

def has_string_dates(daily: list) -> bool:
    return all(isinstance(p.get("date"), str) for p in daily)

Try / catch

try:
    validate_state(state)
except StarHistoryError as exc:
    if "reconstruction date must be a string" in str(exc):
        # coerce date objects to ISO strings, then re-validate
        ...

Prevention

When it happens

Trigger: validate_state(state) where a daily point has date: 20240101 (number), date: null, or date: {"year": 2024, ...}.

Common situations: Programmatic generation where a datetime.date object was serialized incorrectly or an int-encoded date was used to save space; hand-edited files.

Related errors


AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14). Data as JSON: /api/errors/7ce5eff5f26712f3. Report an issue: GitHub.