MemPalace/mempalace · error · ValueError

valid_to={ended!r} is before valid_from={valid_from!r}; an i

Error message

valid_to={ended!r} is before valid_from={valid_from!r}; an inverted interval would be invisible to every KG query

What it means

Raised by KnowledgeGraph.end_fact() when the `ended` timestamp would close an open triple before that triple's stored valid_from. The method loads all open rows (valid_to IS NULL) for the subject/predicate/object and rejects if _temporal_end_key(ended) < _temporal_start_key(valid_from) for any of them, because the resulting inverted interval would be invisible to every query.

Source

Thrown at mempalace/knowledge_graph.py:359

        obj_id = self._entity_id(obj)
        pred = predicate.lower().replace(" ", "_")
        ended = sanitize_iso_temporal(ended or date.today().isoformat(), "ended")

        with self._lock:
            conn = self._conn()
            with conn:
                rows = conn.execute(
                    "SELECT id, valid_from FROM triples "
                    "WHERE subject=? AND predicate=? AND object=? AND valid_to IS NULL",
                    (sub_id, pred, obj_id),
                ).fetchall()

                for row in rows:
                    valid_from = row["valid_from"]
                    if valid_from is not None and _temporal_end_key(ended) < _temporal_start_key(
                        valid_from
                    ):
                        raise ValueError(
                            f"valid_to={ended!r} is before valid_from={valid_from!r}; "
                            "an inverted interval would be invisible to every KG query"
                        )

                conn.execute(
                    "UPDATE triples SET valid_to=? "
                    "WHERE subject=? AND predicate=? AND object=? AND valid_to IS NULL",
                    (ended, sub_id, pred, obj_id),
                )

    def supersede(
        self,
        subject: str,
        predicate: str,
        old_obj: str,
        new_obj: str,
        at: str = None,
        confidence: float = 1.0,

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Verify the stored start: query the triples table for the open row's valid_from
  2. Correct `ended` to be >= the stored valid_from
  3. If the stored valid_from itself is wrong, fix or delete that triple first (repair tooling), then end the fact

Example fix

# before
kg.end_fact("Alice", "works_at", "Acme", ended="2023-01-01")  # open triple started 2024-01-01

# after
kg.end_fact("Alice", "works_at", "Acme", ended="2024-07-01")  # ended >= stored valid_from
Defensive patterns

Strategy: validation

Validate before calling

rows = kg.query_open_triples(subject, predicate, obj)  # or SELECT valid_from WHERE valid_to IS NULL
earliest = min((r["valid_from"] for r in rows if r["valid_from"]), default=None)
if earliest and ended < earliest:
    ended = earliest  # clamp, or fix upstream data

Try / catch

try:
    kg.end_fact(...)
except ValueError as e:
    if "inverted interval" in str(e):
        # fetch stored valid_from, correct `ended`, retry
        ...
    raise

Prevention

When it happens

Trigger: kg.end_fact("Alice", "works_at", "Acme", ended="2023-01-01") when the open triple was recorded with valid_from="2024-01-01" — the end precedes the recorded start.

Common situations: Backfilling history after the fact with a wrong end date; clock skew between the machine that recorded the fact and the one closing it; importing legacy data where the fact's start was already wrong (too late).

Related errors


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