{"record":{"id":"a2a28ab22c35709f","repo":"MemPalace/mempalace","slug":"field-name-value-r-is-not-a-valid-iso-8601-dat","errorCode":null,"errorMessage":"{field_name}={value!r} is not a valid ISO-8601 date or UTC datetime (expected YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)","messagePattern":"(.+?)=(.+?) is not a valid ISO-8601 date or UTC datetime \\(expected YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/config.py","lineNumber":188,"sourceCode":"    - ``YYYY-MM-DDTHH:MM:SSZ``\n    - ``YYYY-MM-DDTHH:MM:SS+00:00`` normalized to ``...Z``\n\n    Partial dates are rejected because KG queries compare TEXT temporal values.\n    Non-canonical datetime forms are rejected because mixed temporal string\n    formats can silently return wrong KG query results.\n    \"\"\"\n\n    if value is None or value == \"\":\n        return value\n    if not isinstance(value, str):\n        raise ValueError(f\"{field_name} must be a string\")\n\n    value = value.strip()\n\n    try:\n        _validate_iso_temporal_calendar(value)\n    except ValueError:\n        raise ValueError(\n            f\"{field_name}={value!r} is not a valid ISO-8601 date or UTC datetime \"\n            \"(expected YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)\"\n        ) from None\n\n    if value.endswith(\"+00:00\"):\n        value = f\"{value[:-6]}Z\"\n\n    return value\n\n\ndef sanitize_iso_date(value, field_name: str = \"date\"):\n    \"\"\"Backward-compatible wrapper for ISO temporal validation.\n\n    Historically this accepted only full dates. It now also accepts canonical\n    UTC datetimes, but the old name is kept so existing imports continue to\n    work.\n    \"\"\"\n","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/config.py#L170-L206","documentation":"Raised by sanitize_iso_temporal() when the string does not parse as a calendar-valid ISO-8601 date or UTC datetime (checked by _validate_iso_temporal_calendar). The KG stores temporal values as TEXT and relies on one canonical shape for lexicographic comparison; only full dates (YYYY-MM-DD) or exact UTC datetimes (YYYY-MM-DDTHH:MM:SSZ, or +00:00 which is normalized to Z) are accepted. Partial dates (YYYY-MM), local-time datetimes without a Z/offset, and calendar-invalid values (2026-02-30) are all rejected because mixed formats would silently return wrong query results.","triggerScenarios":"Passing '2026-08' (partial month), '2026-08-14T10:00:00' (no timezone), '2026-13-01' (invalid month), '14/08/2026' (non-ISO order), or any string datetime.fromisoformat-style parsing would accept but that lacks UTC designation.","commonSituations":"Feeding user-typed dates like 'Aug 2026'; forwarding ISO strings from other APIs that omit the timezone; legacy data using local-time timestamps; date components out of range.","solutions":["Use YYYY-MM-DD for whole-day precision, or YYYY-MM-DDTHH:MM:SSZ for a moment in time","Convert local times to UTC before passing: dt.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')","Expand partial dates to a full day (first/last day of the month) at the caller, since the KG deliberately rejects partial precision","Run the value through datetime.strptime(value, '%Y-%m-%d') (or the full format) first to catch errors early with a clearer message"],"exampleFix":"# before\nkg.add_fact(s, p, o, valid_from=\"2026-08\")\n\n# after\nkg.add_fact(s, p, o, valid_from=\"2026-08-01\")","handlingStrategy":"validation","validationCode":"from datetime import datetime, timezone\n\n# Canonicalize before the call:\ndef to_canonical(s: str) -> str:\n    try:\n        datetime.strptime(s, \"%Y-%m-%d\")\n        return s\n    except ValueError:\n        pass\n    dt = datetime.fromisoformat(s.replace(\"Z\", \"+00:00\"))\n    if dt.tzinfo is None:\n        raise SystemExit(\"attach a timezone: pass UTC '...Z' values\")\n    return dt.astimezone(timezone.utc).strftime(\"%Y-%m-%dT%H:%M:%SZ\")","typeGuard":"def is_canonical_temporal(value: str) -> bool:\n    try:\n        datetime.strptime(value, \"%Y-%m-%d\")\n        return True\n    except ValueError:\n        pass\n    try:\n        datetime.strptime(value, \"%Y-%m-%dT%H:%M:%SZ\")\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    t = sanitize_iso_temporal(v, \"valid_from\")\nexcept ValueError as exc:\n    if \"not a valid ISO-8601\" in str(exc):\n        t = sanitize_iso_temporal(to_canonical(v), \"valid_from\")\n    else:\n        raise","preventionTips":["Store dates as YYYY-MM-DD and moments as ...T00:00:00Z — nothing partial","Always convert local times to UTC before writing","Validate with datetime.strptime at the caller for clearer errors than the library's"],"tags":["validation","temporal","iso-8601","knowledge-graph","mempalace"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}