{"record":{"id":"cee15bb45c0e56b3","repo":"666ghj/MiroFish","slug":"label-is-not-a-valid-utc-timestamp","errorCode":null,"errorMessage":"{label} is not a valid UTC timestamp","messagePattern":"(.+?) is not a valid UTC timestamp","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":323,"sourceCode":"\n\ndef _parse_github_timestamp(value: str) -> datetime:\n    try:\n        parsed = datetime.fromisoformat(value.replace(\"Z\", \"+00:00\"))\n    except ValueError as exc:\n        raise StarHistoryError(\"GitHub returned an invalid star timestamp\") from exc\n    if parsed.tzinfo is None or parsed.utcoffset() != timedelta(0):\n        raise StarHistoryError(\"GitHub star timestamp was not UTC\")\n    return parsed.astimezone(UTC)\n\n\ndef _parse_state_timestamp(value: Any, label: str) -> datetime:\n    if not isinstance(value, str) or not STATE_TIMESTAMP_RE.fullmatch(value):\n        raise StarHistoryError(f\"{label} must use YYYY-MM-DDTHH:MM:SSZ\")\n    try:\n        parsed = datetime.strptime(value, \"%Y-%m-%dT%H:%M:%SZ\").replace(tzinfo=UTC)\n    except ValueError as exc:\n        raise StarHistoryError(f\"{label} is not a valid UTC timestamp\") from exc\n    return parsed\n\n\ndef _format_state_timestamp(value: datetime) -> str:\n    normalized = _normalize_now(value)\n    return normalized.strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n\n\ndef _normalize_now(value: datetime) -> datetime:\n    if value.tzinfo is None or value.utcoffset() != timedelta(0):\n        raise StarHistoryError(\"clock must return a UTC datetime\")\n    return value.astimezone(UTC).replace(microsecond=0)\n\n\ndef _expect_keys(value: Mapping[str, Any], expected: set[str], label: str) -> None:\n    actual = set(value)\n    if actual != expected:\n        raise StarHistoryError(f\"{label} contains missing or unknown fields\")","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L305-L341","documentation":"A state timestamp passed the strict regex (digits in the right slots) but datetime.strptime still raised ValueError — i.e. the value is structurally right yet semantically invalid, like '2024-02-30T00:00:00Z' or '2024-01-15T25:61:00Z'. _parse_state_timestamp chains it into StarHistoryError(f'{label} is not a valid UTC timestamp').","triggerScenarios":"Hand-edited state file with an impossible date (Feb 30, April 31) or out-of-range hour/minute/second; a generator that zero-pads arbitrary integers without range checks.","commonSituations":"Manual edits to the state JSON; a buggy external tool that fabricates timestamps by string formatting instead of using datetime.strftime.","solutions":["Regenerate the state file instead of hand-editing it","Fix the specific impossible timestamp (check Feb 29 in non-leap years)","Make any external generator use datetime.strftime('%Y-%m-%dT%H:%M:%SZ') so ranges are always valid"],"exampleFix":"// before (state file)\n\"generated_at\": \"2024-02-30T10:30:00Z\"\n\n// after (state file)\n\"generated_at\": \"2024-02-29T10:30:00Z\"","handlingStrategy":"validation","validationCode":"from datetime import datetime\n\ndef state_timestamp_semantically_valid(value: str) -> bool:\n    try:\n        datetime.strptime(value, \"%Y-%m-%dT%H:%M:%SZ\")\n        return True\n    except ValueError:\n        return False\n\n# run after the regex check — catches 2024-02-30, hour 25, etc.","typeGuard":"def is_valid_state_timestamp(value: object) -> TypeGuard[str]:\n    return (\n        isinstance(value, str)\n        and bool(STATE_TIMESTAMP_RE.fullmatch(value))\n        and state_timestamp_semantically_valid(value)\n    )","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"not a valid UTC timestamp\" in str(exc):\n        # regex-passed but impossible date (e.g. Feb 30) — file is corrupt; rebuild\n        regenerate_state_file()\n    else:\n        raise","preventionTips":["Produce timestamps only via datetime.strftime, never by string concatenation of numbers","Watch leap-year edges (Feb 29) in hand-made test data"],"tags":["state-file","validation","datetime","star-history"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}