666ghj/MiroFish · error · StarHistoryError

snapshot timestamps must be strictly increasing

Error message

snapshot timestamps must be strictly increasing

What it means

Raised by validate_state() when snapshot 'at' timestamps are not strictly increasing across the snapshots array (a duplicate or out-of-order timestamp fails). Snapshots are appended once per INTERVAL_DAYS run, so order reflects chronological append order.

Source

Thrown at scripts/star_history.py:435

            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
    first_snapshot: datetime | None = None
    for raw_snapshot in snapshots:
        if not isinstance(raw_snapshot, dict):
            raise StarHistoryError("snapshot must be an object")
        _expect_keys(raw_snapshot, {"at", "stars"}, "snapshot")
        snapshot_at = _parse_state_timestamp(raw_snapshot["at"], "snapshot.at")
        _strict_non_negative_int(raw_snapshot["stars"], "snapshot stars")
        if previous_snapshot is not None and snapshot_at <= previous_snapshot:
            raise StarHistoryError("snapshot timestamps must be strictly increasing")
        if first_snapshot is None:
            first_snapshot = snapshot_at
        previous_snapshot = snapshot_at

    if first_snapshot is not None:
        if first_snapshot < generated_at:
            raise StarHistoryError("first snapshot cannot predate reconstruction")
        if previous_day is not None and previous_day >= first_snapshot.date():
            raise StarHistoryError("reconstruction dates must predate snapshots")


def canonical_state_bytes(state: Mapping[str, Any]) -> bytes:
    validate_state(state)
    return (
        json.dumps(state, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
    ).encode("utf-8")

View on GitHub (pinned to b5b53acc57)

Solutions

  1. Sort snapshots by 'at' ascending and remove duplicate timestamps before writing state
  2. When merging histories, dedupe on 'at' keeping the latest write
  3. Re-run the snapshot interval to append a fresh, later timestamp

Example fix

# before
snapshots = snapshots + merged_snapshots  # unsorted concat
# after
snapshots = sorted({s["at"]: s for s in snapshots + merged_snapshots}.values(), key=lambda s: s["at"])
Defensive patterns

Strategy: validation

Validate before calling

snaps = sorted({s["at"]: s for s in snaps}.values(), key=lambda s: s["at"])

Type guard

def snapshot_timestamps_increasing(snaps: list) -> bool:
    return all(snaps[i]["at"] < snaps[i + 1]["at"] for i in range(len(snaps) - 1))

Try / catch

try:
    validate_state(state)
except StarHistoryError as exc:
    if "snapshot timestamps must be strictly increasing" in str(exc):
        # sort by 'at' and deduplicate timestamps, then re-validate
        ...

Prevention

When it happens

Trigger: validate_state(state) where snapshots[i]['at'] <= snapshots[i-1]['at'] — e.g. two snapshots with identical timestamps or a list sorted by stars instead of time.

Common situations: Merging state files by naive concatenation without re-sorting; a state rewrite that lost ordering; clock skew causing an equal or earlier timestamp on a later run.

Related errors


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