{"record":{"id":"35a767fcf2153561","repo":"MemPalace/mempalace","slug":"field-name-must-be-a-string","errorCode":null,"errorMessage":"{field_name} must be a string","messagePattern":"(.+?) must be a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/config.py","lineNumber":181,"sourceCode":"    \"\"\"Validate an ISO-8601 date or canonical UTC datetime string.\n\n    Accepts ``None`` and ``\"\"`` as pass-through values.\n\n    Accepted non-empty string forms:\n\n    - ``YYYY-MM-DD``\n    - ``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\"):","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/config.py#L163-L199","documentation":"Raised by sanitize_iso_temporal() when a temporal parameter (as_of, valid_from, valid_to, ended) is not None, not the empty string, and not a str — e.g. an int timestamp or a datetime object. Temporal values are stored as canonical TEXT and compared lexicographically, so the API accepts only strings it can normalize; pass None/'' for 'no value' and format datetimes yourself.","triggerScenarios":"Calling a KG API with valid_from=1723680000 (unix int), valid_from=datetime.now(), or a date object instead of the string form; only None and \"\" pass through unvalidated.","commonSituations":"Programmatic callers forwarding datetime objects from their ORM; unix timestamps from logs; JSON payloads where numbers were auto-typed.","solutions":["Format datetimes to the canonical string yourself: dt.strftime('%Y-%m-%dT%H:%M:%SZ') (UTC) or date.isoformat()","Pass None (or \"\") when the temporal bound is open, not 0 or false","Coerce at the boundary: str(value) is not enough — it must be ISO-formatted"],"exampleFix":"# before\nkg.add_fact(s, p, o, valid_from=datetime(2026, 1, 1))\n\n# after\nkg.add_fact(s, p, o, valid_from=\"2026-01-01T00:00:00Z\")","handlingStrategy":"type-guard","validationCode":"from datetime import datetime, timezone\n\n# Normalize any datetime-like input before the call:\nif isinstance(v, datetime):\n    v = v.astimezone(timezone.utc).strftime(\"%Y-%m-%dT%H:%M:%SZ\")\nelif not isinstance(v, (str, type(None))):\n    raise SystemExit(\"temporal values must be ISO strings or None\")","typeGuard":"def is_temporal_arg(value) -> bool:\n    return value is None or value == \"\" or isinstance(value, str)","tryCatchPattern":"try:\n    t = sanitize_iso_temporal(valid_from, \"valid_from\")\nexcept ValueError as exc:\n    if \"must be a string\" in str(exc):\n        t = sanitize_iso_temporal(str(valid_from), \"valid_from\")  # only if str(v) is ISO\n    else:\n        raise","preventionTips":["Convert datetime objects to ISO strings at your API boundary","Use None (not 0) for open temporal bounds","Standardize on UTC everywhere in the pipeline"],"tags":["validation","temporal","knowledge-graph","mempalace"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}