headroomlabs-ai/headroom · error · ValueError
Memory {old_memory_id} not found
Error message
Memory {old_memory_id} not found What it means
Raised by detach_supersession when the old memory ID is not present in the memories table. The operation looks up both endpoints inside a BEGIN IMMEDIATE transaction; a missing old endpoint means there is no supersession edge to detach.
Source
Thrown at headroom/memory/adapters/sqlite.py:731
This is an explicit repair operation. It never infers identity from
content or embedding similarity and leaves neighboring chain edges
untouched.
"""
if old_memory_id == new_memory_id:
raise ValueError("A memory cannot supersede itself")
with self._get_conn() as conn:
conn.execute("BEGIN IMMEDIATE")
rows = conn.execute(
"SELECT * FROM memories WHERE id IN (?, ?)",
(old_memory_id, new_memory_id),
).fetchall()
memories = {row["id"]: self._row_to_memory(row) for row in rows}
old_memory = memories.get(old_memory_id)
new_memory = memories.get(new_memory_id)
if old_memory is None:
raise ValueError(f"Memory {old_memory_id} not found")
if new_memory is None:
raise ValueError(f"Memory {new_memory_id} not found")
if old_memory.superseded_by != new_memory_id or new_memory.supersedes != old_memory_id:
raise ValueError(
f"Memories {old_memory_id} and {new_memory_id} do not form "
"a reciprocal supersession edge"
)
conn.execute(
"UPDATE memories SET valid_until = NULL, superseded_by = NULL WHERE id = ?",
(old_memory_id,),
)
conn.execute(
"UPDATE memories SET supersedes = NULL WHERE id = ?",
(new_memory_id,),
)
old_memory.valid_until = NoneView on GitHub (pinned to 322425c43b)
Solutions
- Verify both IDs exist (SELECT) before invoking detach_supersession.
- Regenerate edge pairs from the current database state (superseded_by/supersedes columns) rather than cached lists.
- Catch the ValueError and treat it as 'nothing to detach', logging for audit.
Example fix
// before
await store.detach_supersession(old_id, new_id)
// after
try:
await store.detach_supersession(old_id, new_id)
except ValueError as e:
logger.info("edge already gone: %s", e) Defensive patterns
Strategy: try-catch
Validate before calling
rows = await store.get_many([old_memory_id, new_memory_id])
if old_memory_id not in rows or new_memory_id not in rows:
logger.warning("endpoint missing; nothing to detach")
return Try / catch
try:
await store.detach_supersession(old_id, new_id)
except ValueError as e:
if "not found" in str(e):
logger.info("edge endpoints missing; treating as detached")
else:
raise Prevention
- Derive edge pairs from live superseded_by/supersedes columns, not cached logs.
- Idempotent repair loops should tolerate already-removed rows.
When it happens
Trigger: Calling detach_supersession with an ID that was deleted (possibly by the supersession chain's own cleanup); IDs from another database or with trailing whitespace/case differences; stale references after a data migration.
Common situations: Repair scripts replaying old edge logs after rows were removed; cross-environment ID confusion; chain-compaction having already removed the node.
Related errors
- Memory with ID {old_memory_id} not found
- Memory {new_memory_id} not found
- A memory cannot supersede itself
- Memories {old_memory_id} and {new_memory_id} do not form a r
- Memories {old_memory.id} and {new_memory.id} do not form a r
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/6e3d0b6aeef39ff4.
Report an issue: GitHub.