MemPalace/mempalace · error · ValueError
{field_name} must be an ISO date string (e.g. '2026-04-01' o
Error message
{field_name} must be an ISO date string (e.g. '2026-04-01' or '2026-04-01T09:30:00'), got {value!r} What it means
ValueError from parse_date_bound (mempalace/date_window.py:62): the value is a string but datetime.fromisoformat cannot parse it after stripping an optional trailing Z/z. The message shows the field name, the expected formats, and the offending value verbatim. The accepted grammar is narrow by design: a date, an ISO timestamp (optional fractional seconds), optional Z or ±HH:MM offset — basic-format (no dashes), week dates, and other ISO 8601 exotica are rejected on the Python 3.9 floor.
Source
Thrown at mempalace/date_window.py:62
clear error, mirroring the wing/room sanitizers.
"""
if value is None:
return None
if not isinstance(value, str):
raise ValueError(f"{field_name} must be an ISO date string")
value = value.strip()
if not value:
return None
# datetime.fromisoformat before Python 3.11 rejects a trailing "Z" (Zulu),
# and appending "+00:00" would break a date-only value on 3.9/3.10
# ("2026-04-01+00:00" is rejected there). Any offset is dropped below for
# wall-clock comparison anyway, so just strip a trailing Z/z; both date and
# date-time Zulu inputs then parse on the 3.9 floor.
iso = value[:-1] if value.endswith(("Z", "z")) else value
try:
parsed = datetime.fromisoformat(iso)
except ValueError as exc:
raise ValueError(
f"{field_name} must be an ISO date string "
f"(e.g. '2026-04-01' or '2026-04-01T09:30:00'), got {value!r}"
) from exc
if parsed.tzinfo is not None:
parsed = parsed.replace(tzinfo=None)
return parsed
def parse_window(since: Optional[str] = None, before: Optional[str] = None):
"""Parse a ``[since, before)`` pair, rejecting an inverted window.
Returns ``(since_dt, before_dt)`` — either side ``None`` when absent.
Raises ``ValueError`` (naming the offending field or the inversion) so
callers surface the same message everywhere a window is accepted.
"""
since_dt = parse_date_bound(since, "since")
before_dt = parse_date_bound(before, "before")
if since_dt is not None and before_dt is not None and since_dt >= before_dt:View on GitHub (pinned to 06cb6987f0)
Solutions
- Use extended ISO format: 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS' (fractional seconds and a Z or ±HH:MM offset allowed).
- Normalize user input before calling: parse with dateutil or strptime using the actual input format, then pass .isoformat().
- Check the got {value!r} portion of the message to spot invisible characters or wrong separators.
Example fix
# before
search(since="2026/04/01")
# after
from datetime import datetime
search(since=datetime.strptime("2026/04/01", "%Y/%m/%d").date().isoformat()) Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime
def normalize_iso(value: str) -> str:
v = value.strip()
if v.endswith(("Z", "z")):
v = v[:-1]
datetime.fromisoformat(v) # raises with context if unparseable
return v Type guard
def is_iso_date_str(value: object) -> bool:
if not isinstance(value, str) or not value.strip():
return False
try:
datetime.fromisoformat(value.strip().rstrip("Zz"))
return True
except ValueError:
return False Try / catch
try:
parse_window(since, before)
except ValueError as exc:
# user-facing message already includes field name, expected format, and got-value
return error_response(str(exc)) Prevention
- Accept only extended ISO format ('YYYY-MM-DD' / 'YYYY-MM-DDTHH:MM:SS'); reject basic format and week dates by design.
- Parse locale-formatted user input with strptime/dateutil first, then pass .isoformat().
- Log the repr of rejected values to catch invisible whitespace/encoding issues.
When it happens
Trigger: Passing '2026/04/01' (slashes), '20260401' (basic format), '2026-W14-2' (week date), 'Apr 1 2026', or any locale-formatted date; timestamps with a space separator instead of 'T' where fromisoformat rejects them; stray characters or double Z.
Common situations: User-supplied date strings from chat/CLI entering search filters; data exported from spreadsheets with regional formats; assuming full ISO 8601 support when the contract is the extended format subset only.
Related errors
- {field_name} must be an ISO date string
- since ({since!r}) must be earlier than before ({before!r})
- {type(self).name} does not advertise supports_namespace_isol
- operator {key!r} not supported by chroma backend
- ChromaBackend has been closed
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/82abdd71f2d9c101.
Report an issue: GitHub.