666ghj/MiroFish · error · StarHistoryError

reconstruction.daily must be a list

Error message

reconstruction.daily must be a list

What it means

Raised by validate_state() when reconstruction['daily'] is not a JSON array. 'daily' holds the reconstructed per-day star count points, each an object with 'date' and 'stars'; anything other than a list (dict, string, number, null) fails immediately before per-point validation.

Source

Thrown at scripts/star_history.py:392

    if not isinstance(reconstruction, dict):
        raise StarHistoryError("reconstruction must be an object")
    _expect_keys(
        reconstruction,
        {"method", "generated_at", "daily"},
        "reconstruction",
    )
    reconstruction_method = reconstruction["method"]
    if reconstruction_method not in {
        "current_stargazers_starred_at",
        "aggregate_snapshot_only",
    }:
        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")

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Make daily a JSON array of {"date": "YYYY-MM-DD", "stars": <int>} objects (it may be empty for aggregate_snapshot_only)
  2. If using a date-keyed map internally, convert it to a list sorted by date before writing state
  3. Regenerate the state with the current script rather than hand-transforming it

Example fix

// before
"daily": {"2024-01-01": 10, "2024-01-02": 25}
// after
"daily": [{"date": "2024-01-01", "stars": 10}, {"date": "2024-01-02", "stars": 25}]
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(state["reconstruction"].get("daily"), list):
    daily = [] if state["reconstruction"].get("daily") is None else sorted(
        [{"date": d, "stars": s} for d, s in state["reconstruction"]["daily"].items()],
        key=lambda p: p["date"],
    )

Type guard

def is_daily_list(value) -> bool:
    return isinstance(value, list) and all(
        isinstance(p, dict) and set(p) == {"date", "stars"} for p in value
    )

Try / catch

try:
    validate_state(state)
except StarHistoryError as exc:
    if "reconstruction.daily must be a list" in str(exc):
        # normalize a date-keyed map to a sorted list, then re-validate
        ...

Prevention

When it happens

Trigger: validate_state(state) where reconstruction.daily is an object keyed by date (e.g. {"2024-01-01": 10}) instead of a list of points, or a null/string value from a partial hand edit.

Common situations: Converting the daily data to a date-keyed map for convenience and forgetting to convert back; merging state files with a tool that changed the array to an object; older producers that emitted a different structure.

Related errors


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