666ghj/MiroFish · error · StarHistoryError
{label} must use YYYY-MM-DDTHH:MM:SSZ
Error message
{label} must use YYYY-MM-DDTHH:MM:SSZ What it means
_parse_state_timestamp validates persisted-state timestamps against STATE_TIMESTAMP_RE (r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$') and raises StarHistoryError(f'{label} must use YYYY-MM-DDTHH:MM:SSZ') when the value is not a string or does not fullmatch. This guards the history-state JSON file against format drift (fractional seconds, offsets, missing Z) before strptime runs.
Source
Thrown at scripts/star_history.py:319
def _strict_non_negative_int(value: Any, label: str) -> int:
if type(value) is not int or value < 0:
raise StarHistoryError(f"{label} must be a non-negative integer")
return value
def _parse_github_timestamp(value: str) -> datetime:
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise StarHistoryError("GitHub returned an invalid star timestamp") from exc
if parsed.tzinfo is None or parsed.utcoffset() != timedelta(0):
raise StarHistoryError("GitHub star timestamp was not UTC")
return parsed.astimezone(UTC)
def _parse_state_timestamp(value: Any, label: str) -> datetime:
if not isinstance(value, str) or not STATE_TIMESTAMP_RE.fullmatch(value):
raise StarHistoryError(f"{label} must use YYYY-MM-DDTHH:MM:SSZ")
try:
parsed = datetime.strptime(value, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=UTC)
except ValueError as exc:
raise StarHistoryError(f"{label} is not a valid UTC timestamp") from exc
return parsed
def _format_state_timestamp(value: datetime) -> str:
normalized = _normalize_now(value)
return normalized.strftime("%Y-%m-%dT%H:%M:%SZ")
def _normalize_now(value: datetime) -> datetime:
if value.tzinfo is None or value.utcoffset() != timedelta(0):
raise StarHistoryError("clock must return a UTC datetime")
return value.astimezone(UTC).replace(microsecond=0)
View on GitHub (pinned to b5b53acc57)
Solutions
- Reset the state file to let the script regenerate it from scratch (losing incremental history is often acceptable)
- Or fix the offending timestamp(s) in the state file to strict 'YYYY-MM-DDTHH:MM:SSZ' form
- Identify the writer that produced the bad format and route it through _format_state_timestamp
Example fix
// before (state file) "generated_at": "2024-01-15T10:30:00.123456+00:00" // after (state file) "generated_at": "2024-01-15T10:30:00Z"
Defensive patterns
Strategy: validation
Validate before calling
import re
STATE_TIMESTAMP_RE = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$")
bad = [k for k, v in state_flat_timestamps.items() if not (isinstance(v, str) and STATE_TIMESTAMP_RE.fullmatch(v))]
if bad:
raise StarHistoryError(f"timestamps not in YYYY-MM-DDTHH:MM:SSZ: {bad}") Type guard
def is_state_timestamp(value: object) -> TypeGuard[str]:
return isinstance(value, str) and bool(STATE_TIMESTAMP_RE.fullmatch(value)) Try / catch
try:
validate_state(state)
except StarHistoryError as exc:
if "must use YYYY-MM-DDTHH:MM:SSZ" in str(exc):
regenerate_state_file()
else:
raise Prevention
- Never hand-edit timestamps in the state file; regenerate the file instead
- Any external writer must use strftime('%Y-%m-%dT%H:%M:%SZ') — no fractional seconds, no offsets
When it happens
Trigger: A state file containing 'generated_at': '2024-01-15T10:30:00' (no Z), '...:00.123456Z' (fractional seconds), '...+00:00' (offset instead of Z), or a non-string; also hand-edited state files.
Common situations: State file written by an older/newer version of the script with a different timestamp format; someone edited the JSON by hand and pasted a timezone-offset timestamp; a different tool regenerated the file with fromisoformat-style output.
Related errors
- {label} is not a valid UTC timestamp
- GitHub returned an invalid star timestamp
- GitHub star timestamp was not UTC
- clock must return a UTC datetime
- {label} contains missing or unknown fields
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/222cb51b1cd49174.
Report an issue: GitHub.