MemPalace/mempalace · error · ValueError
{field_name} must be a string
Error message
{field_name} must be a string What it means
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.
Source
Thrown at mempalace/config.py:181
"""Validate an ISO-8601 date or canonical UTC datetime string.
Accepts ``None`` and ``""`` as pass-through values.
Accepted non-empty string forms:
- ``YYYY-MM-DD``
- ``YYYY-MM-DDTHH:MM:SSZ``
- ``YYYY-MM-DDTHH:MM:SS+00:00`` normalized to ``...Z``
Partial dates are rejected because KG queries compare TEXT temporal values.
Non-canonical datetime forms are rejected because mixed temporal string
formats can silently return wrong KG query results.
"""
if value is None or value == "":
return value
if not isinstance(value, str):
raise ValueError(f"{field_name} must be a string")
value = value.strip()
try:
_validate_iso_temporal_calendar(value)
except ValueError:
raise ValueError(
f"{field_name}={value!r} is not a valid ISO-8601 date or UTC datetime "
"(expected YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)"
) from None
if value.endswith("+00:00"):
value = f"{value[:-6]}Z"
return value
def sanitize_iso_date(value, field_name: str = "date"):View on GitHub (pinned to 06cb6987f0)
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
Example fix
# before kg.add_fact(s, p, o, valid_from=datetime(2026, 1, 1)) # after kg.add_fact(s, p, o, valid_from="2026-01-01T00:00:00Z")
Defensive patterns
Strategy: type-guard
Validate before calling
from datetime import datetime, timezone
# Normalize any datetime-like input before the call:
if isinstance(v, datetime):
v = v.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
elif not isinstance(v, (str, type(None))):
raise SystemExit("temporal values must be ISO strings or None") Type guard
def is_temporal_arg(value) -> bool:
return value is None or value == "" or isinstance(value, str) Try / catch
try:
t = sanitize_iso_temporal(valid_from, "valid_from")
except ValueError as exc:
if "must be a string" in str(exc):
t = sanitize_iso_temporal(str(valid_from), "valid_from") # only if str(v) is ISO
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: Programmatic callers forwarding datetime objects from their ORM; unix timestamps from logs; JSON payloads where numbers were auto-typed.
Related errors
- {field_name}={value!r} is not a valid ISO-8601 date or UTC d
- valid_to={valid_to!r} is before valid_from={valid_from!r}; a
- valid_to={ended!r} is before valid_from={valid_from!r}; an i
- at={boundary!r} is before valid_from={valid_from!r}; an inve
- pass inline text or a file, not both
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/35a767fcf2153561.
Report an issue: GitHub.