{"record":{"id":"3983f92c1cc9a6eb","repo":"MemPalace/mempalace","slug":"valid-to-valid-to-r-is-before-valid-from-valid","errorCode":null,"errorMessage":"valid_to={valid_to!r} is before valid_from={valid_from!r}; an inverted interval would be invisible to every KG query","messagePattern":"valid_to=(.+?) is before valid_from=(.+?); an inverted interval would be invisible to every KG query","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/knowledge_graph.py","lineNumber":283,"sourceCode":"\n        Examples:\n            add_triple(\"Max\", \"child_of\", \"Alice\", valid_from=\"2015-04-01\")\n            add_triple(\"Max\", \"does\", \"swimming\", valid_from=\"2025-01-01\")\n            add_triple(\"Alice\", \"worried_about\", \"Max injury\", valid_from=\"2026-01-01\")\n        \"\"\"\n\n        valid_from = sanitize_iso_temporal(valid_from, \"valid_from\")\n        valid_to = sanitize_iso_temporal(valid_to, \"valid_to\")\n\n        # Reject inverted intervals. Use temporal comparison keys rather than\n        # raw string comparison so legacy date-only values and canonical UTC\n        # datetimes can safely coexist.\n        if (\n            valid_from is not None\n            and valid_to is not None\n            and _temporal_end_key(valid_to) < _temporal_start_key(valid_from)\n        ):\n            raise ValueError(\n                f\"valid_to={valid_to!r} is before valid_from={valid_from!r}; \"\n                \"an inverted interval would be invisible to every KG query\"\n            )\n\n        sub_id = self._entity_id(subject)\n        obj_id = self._entity_id(obj)\n        pred = predicate.lower().replace(\" \", \"_\")\n\n        # Auto-create entities if they don't exist\n        with self._lock:\n            conn = self._conn()\n            with conn:\n                conn.execute(\n                    \"INSERT OR IGNORE INTO entities (id, name) VALUES (?, ?)\",\n                    (sub_id, subject),\n                )\n                conn.execute(\n                    \"INSERT OR IGNORE INTO entities (id, name) VALUES (?, ?)\",","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/knowledge_graph.py#L265-L301","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nkg.add_fact(\"Alice\", \"works_at\", \"Acme\", valid_from=\"2024-06-01\", valid_to=\"2024-01-01\")\n\n# after\nkg.add_fact(\"Alice\", \"works_at\", \"Acme\", valid_from=\"2024-01-01\", valid_to=\"2024-06-01\")  # from <= to","handlingStrategy":"validation","validationCode":"from mempalace.knowledge_graph import _temporal_end_key, _temporal_start_key  # or compare sanitized values\n\ndef valid_interval(valid_from, valid_to):\n    return valid_to is None or valid_from is None or _temporal_end_key(valid_to) >= _temporal_start_key(valid_from)\n\nif valid_interval(vf, vt):\n    kg.add_fact(subj, pred, obj, valid_from=vf, valid_to=vt)","typeGuard":null,"tryCatchPattern":"try:\n    kg.add_fact(...)\nexcept ValueError as e:\n    if \"inverted interval\" in str(e):\n        log.warning(\"dropping fact with bad dates: %s\", e)\n        return\n    raise","preventionTips":["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"],"tags":["knowledge-graph","temporal","validation","date"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}