MemPalace/mempalace · error · ValueError
since ({since!r}) must be earlier than before ({before!r})
Error message
since ({since!r}) must be earlier than before ({before!r}) What it means
ValueError from parse_window (mempalace/date_window.py:81): both bounds parsed successfully, but since is not strictly earlier than before (>= comparison fails). The window is half-open [since, before), so an inverted or zero-width window (since == before) is rejected as a caller error. Both values are shown repr()'d in the message.
Source
Thrown at mempalace/date_window.py:81
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:
raise ValueError(f"since ({since!r}) must be earlier than before ({before!r})")
return since_dt, before_dt
def filed_at_in_window(
filed_at, since_dt: Optional[datetime], before_dt: Optional[datetime]
) -> bool:
"""True if a drawer's ``filed_at`` falls in ``[since, before)``.
``since`` is inclusive and ``before`` is exclusive, matching the issue spec.
Parsing (``Z``/offset normalization, tz drop) is delegated to
``parse_date_bound`` so a bound and a ``filed_at`` are compared
identically. A drawer whose ``filed_at`` is missing or unparseable cannot
be confirmed in-window, so it is EXCLUDED whenever a bound is active — a
date-filtered listing must never silently include rows of unknown age.
"""
try:
filed_dt = parse_date_bound(filed_at, "filed_at")
except ValueError:View on GitHub (pinned to 06cb6987f0)
Solutions
- Swap the arguments so since is the earlier bound.
- For a single-day window, use before = since_date + 1 day (e.g. since='2026-04-01', before='2026-04-02') since the window is [since, before).
- Sort/validate bounds at the call site: if start > end, swap or surface a form error to the user.
Example fix
# before (single day, zero-width window) parse_window(since="2026-04-01", before="2026-04-01") # after parse_window(since="2026-04-01", before="2026-04-02")
Defensive patterns
Strategy: validation
Validate before calling
from mempalace.date_window import parse_date_bound
def ordered_window(since, before):
s, b = parse_date_bound(since, "since"), parse_date_bound(before, "before")
if s is not None and b is not None and s >= b:
return None # or raise before hitting parse_window
return s, b Type guard
def is_valid_window(since: object, before: object) -> bool:
try:
parse_window(since, before) # noqa: returns without raising
return True
except ValueError:
return False Try / catch
try:
since_dt, before_dt = parse_window(since, before)
except ValueError as exc:
if "must be earlier" in str(exc):
since, before = before, since # or return a form error
since_dt, before_dt = parse_window(since, before)
else:
raise Prevention
- Remember the window is half-open [since, before): single-day filters need before = day + 1.
- Sort date-picker output (min, max) before building the window.
- Validate window order at the UI/CLI boundary with a clear message.
When it happens
Trigger: Swapping the arguments (parse_window(before=x, since=y) or passing the later date first); an equal-instant pair like since='2026-04-01', before='2026-04-01' expecting an inclusive single-day window; timezone-stripped comparisons where offsets masked the real order (both sides are normalized to naive wall-clock before comparing).
Common situations: UI or CLI date pickers returning start/end in the wrong order; code computing before = since + delta with a negative delta; single-day filters needing [day, day+1) instead of [day, day).
Related errors
- {field_name} must be an ISO date string
- {field_name} must be an ISO date string (e.g. '2026-04-01' o
- {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/71e71bfa67d83de6.
Report an issue: GitHub.