666ghj/MiroFish · error · StarHistoryError

reconstruction date is invalid

Error message

reconstruction date is invalid

What it means

Raised by validate_state() when a daily point's 'date' string cannot be parsed by date.fromisoformat (ValueError chained as StarHistoryError). This catches syntactically invalid dates before the canonical-form check runs.

Source

Thrown at scripts/star_history.py:408

    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

    snapshots = state["snapshots"]
    if not isinstance(snapshots, list):
        raise StarHistoryError("snapshots must be a list")
    previous_snapshot: datetime | None = None

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Use exactly zero-padded 'YYYY-MM-DD' calendar dates
  2. In producers, always derive the string from date.fromisoformat round-trip or d.isoformat()
  3. If you meant to store a timestamp, it belongs in snapshot.at, not reconstruction daily dates

Example fix

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

Strategy: validation

Validate before calling

from datetime import date
for p in daily:
    try:
        date.fromisoformat(p["date"])
    except ValueError:
        raise ValueError(f"invalid date: {p['date']!r}")

Type guard

from datetime import date

def are_valid_iso_dates(daily: list) -> bool:
    try:
        return all(date.fromisoformat(p["date"]) for p in daily) is not None
    except (ValueError, TypeError):
        return False

Try / catch

try:
    validate_state(state)
except StarHistoryError as exc:
    if "reconstruction date is invalid" in str(exc):
        # drop or re-derive bad points, then re-validate
        ...

Prevention

When it happens

Trigger: validate_state(state) where a date string is e.g. '2024-13-01', '2024-1-1', 'not-a-date', or an ISO datetime like '2024-01-01T00:00:00Z' (fromisoformat on date rejects the time part).

Common situations: Writing full timestamps into a date-only field; month/day transposition producing invalid months; string formatting bugs that drop zero padding (caught here or by the canonicality check).

Related errors


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