MemPalace/mempalace · error · ValueError
valid_to={valid_to!r} is before valid_from={valid_from!r}; a
Error message
valid_to={valid_to!r} is before valid_from={valid_from!r}; an inverted interval would be invisible to every KG query What it means
Raised by KnowledgeGraph.add_fact() when the temporal interval is inverted: valid_to resolves to an instant strictly earlier than valid_from. Comparison uses _temporal_start_key/_temporal_end_key so legacy date-only values and canonical UTC datetimes compare correctly. The guard exists because every KG query filters on valid_from <= valid_to, so an inverted interval would be silently invisible.
Source
Thrown at mempalace/knowledge_graph.py:283
Examples:
add_triple("Max", "child_of", "Alice", valid_from="2015-04-01")
add_triple("Max", "does", "swimming", valid_from="2025-01-01")
add_triple("Alice", "worried_about", "Max injury", valid_from="2026-01-01")
"""
valid_from = sanitize_iso_temporal(valid_from, "valid_from")
valid_to = sanitize_iso_temporal(valid_to, "valid_to")
# Reject inverted intervals. Use temporal comparison keys rather than
# raw string comparison so legacy date-only values and canonical UTC
# datetimes can safely coexist.
if (
valid_from is not None
and valid_to is not None
and _temporal_end_key(valid_to) < _temporal_start_key(valid_from)
):
raise ValueError(
f"valid_to={valid_to!r} is before valid_from={valid_from!r}; "
"an inverted interval would be invisible to every KG query"
)
sub_id = self._entity_id(subject)
obj_id = self._entity_id(obj)
pred = predicate.lower().replace(" ", "_")
# Auto-create entities if they don't exist
with self._lock:
conn = self._conn()
with conn:
conn.execute(
"INSERT OR IGNORE INTO entities (id, name) VALUES (?, ?)",
(sub_id, subject),
)
conn.execute(
"INSERT OR IGNORE INTO entities (id, name) VALUES (?, ?)",View on GitHub (pinned to 06cb6987f0)
Solutions
- Check the caller: the two date arguments are almost certainly swapped
- Normalize both values through sanitize_iso_temporal yourself and compare before calling add_fact
- If the end is unknown, pass valid_to=None instead of guessing an early date
- Watch for timezone conversions that move a same-day interval across the date line
Example fix
# before
kg.add_fact("Alice", "works_at", "Acme", valid_from="2024-06-01", valid_to="2024-01-01")
# after
kg.add_fact("Alice", "works_at", "Acme", valid_from="2024-01-01", valid_to="2024-06-01") # from <= to Defensive patterns
Strategy: validation
Validate before calling
from mempalace.knowledge_graph import _temporal_end_key, _temporal_start_key # or compare sanitized values
def valid_interval(valid_from, valid_to):
return valid_to is None or valid_from is None or _temporal_end_key(valid_to) >= _temporal_start_key(valid_from)
if valid_interval(vf, vt):
kg.add_fact(subj, pred, obj, valid_from=vf, valid_to=vt) Try / catch
try:
kg.add_fact(...)
except ValueError as e:
if "inverted interval" in str(e):
log.warning("dropping fact with bad dates: %s", e)
return
raise Prevention
- Always pass valid_from/valid_to as keyword arguments to avoid positional swaps
- Use valid_to=None for open-ended facts instead of guessing an end date
- Normalize all dates to canonical UTC ISO format at ingest
When it happens
Trigger: kg.add_fact("Alice", "works_at", "Acme", valid_from="2024-06-01", valid_to="2024-01-01") — any call where the end precedes the start, including mixed precision like valid_from="2024-06-01T00:00:00Z" with valid_to="2024-06-01" boundary handling.
Common situations: Swapping from/to arguments in caller code; user-typed date ranges entered backwards; timezone shifts (UTC vs local) pushing an end date before a start date; migrating data whose source system stored dates ambiguously.
Related errors
- valid_to={ended!r} is before valid_from={valid_from!r}; an i
- {field_name} must be a string
- {field_name}={value!r} is not a valid ISO-8601 date or UTC d
- at={boundary!r} is before valid_from={valid_from!r}; an inve
- {type(self).name} does not advertise supports_namespace_isol
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/3983f92c1cc9a6eb.
Report an issue: GitHub.