666ghj/MiroFish · error · StarHistoryError
reconstruction date is not canonical
Error message
reconstruction date is not canonical
What it means
Raised by validate_state() when a daily date string parses via date.fromisoformat but does not round-trip to its canonical isoformat() representation. This rejects non-padded or oddly formatted dates like '2024-1-1' or '2024-01-1' that Python may still parse, enforcing one canonical representation.
Source
Thrown at scripts/star_history.py:410
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
first_snapshot: datetime | None = None
for raw_snapshot in snapshots:View on GitHub (pinned to b5b53acc57)
Solutions
- Always produce dates with d.isoformat() or '%Y-%m-%d' formatting
- Fix generator code that formats dates manually
- Validate round-trip equality in tests: date.fromisoformat(s).isoformat() == s
Example fix
# before
f"{point.year}-{point.month}-{point.day}"
# after
point.isoformat() Defensive patterns
Strategy: validation
Validate before calling
for p in daily:
d = date.fromisoformat(p["date"])
if d.isoformat() != p["date"]:
p["date"] = d.isoformat() # canonicalize Type guard
def are_canonical_dates(daily: list) -> bool:
return all(
date.fromisoformat(p["date"]).isoformat() == p["date"] for p in daily
) Try / catch
try:
validate_state(state)
except StarHistoryError as exc:
if "reconstruction date is not canonical" in str(exc):
for p in daily:
p["date"] = date.fromisoformat(p["date"]).isoformat()
validate_state(state) # re-validate after canonicalization
Prevention
- Format dates with d.isoformat() or '%Y-%m-%d', never manual f-strings
- Canonicalize external date strings before storing them
- Assert round-trip equality in serialization tests
When it happens
Trigger: validate_state(state) where date is '2024-1-1', '2024-01-1', or any form where date.fromisoformat(raw).isoformat() != raw.
Common situations: Date formatting code using f"{y}-{m}-{d}" without zero padding; locales/serializers that strip leading zeros; manual date entry.
Related errors
- reconstruction date must be a string
- reconstruction date is invalid
- t('step5.selectAgentFirst')
- graph_id is required
- At least one text chunk is required
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/d73357fa4110450d.
Report an issue: GitHub.