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

  1. Use extended ISO format: 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS' (fractional seconds and a Z or ±HH:MM offset allowed).
  2. Normalize user input before calling: parse with dateutil or strptime using the actual input format, then pass .isoformat().
  3. 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

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


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/82abdd71f2d9c101. Report an issue: GitHub.