headroomlabs-ai/headroom · error · ValueError

Memories {old_memory_id} and {new_memory_id} do not form a r

Error message

Memories {old_memory_id} and {new_memory_id} do not form a reciprocal supersession edge

What it means

Raised by detach_supersession when both memories exist but they do not form a reciprocal supersession edge — i.e. old.superseded_by != new_id or new.supersedes != old_id. The detach operation only removes verified two-way edges and never infers one-sided links, so any asymmetry is rejected.

Source

Thrown at headroom/memory/adapters/sqlite.py:735

        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 = None
        old_memory.superseded_by = None
        new_memory.supersedes = None
        return old_memory, new_memory

View on GitHub (pinned to 322425c43b)

Solutions

  1. Inspect both rows: SELECT superseded_by, supersedes for the pair and confirm they point at each other before detaching.
  2. If the edge is one-sided, repair the dangling column(s) explicitly rather than calling detach_supersession.
  3. For chains where the old memory was superseded by a different memory, call detach with the actual current partner IDs.

Example fix

// before
await store.detach_supersession(old_id, new_id)  # assumed edge

// after
old = await store.get(old_id); new = await store.get(new_id)
if old.superseded_by == new_id and new.supersedes == old_id:
    await store.detach_supersession(old_id, new_id)
else:
    logger.warning("no reciprocal edge; manual repair needed")
Defensive patterns

Strategy: validation

Validate before calling

old = await store.get(old_id); new = await store.get(new_id)
if not (old and new and old.superseded_by == new_id and new.supersedes == old_id):
    logger.warning("not a reciprocal edge; manual repair required")
    return
await store.detach_supersession(old_id, new_id)

Type guard

def is_reciprocal(old: Memory, new: Memory) -> bool:
    return old.superseded_by == new.id and new.supersedes == old.id

Try / catch

try:
    await store.detach_supersession(old_id, new_id)
except ValueError as e:
    if "reciprocal" in str(e):
        # inspect rows and repair dangling pointers manually
        ...

Prevention

When it happens

Trigger: Passing two memories that are adjacent in a chain but whose forward/back pointers disagree (e.g. old.superseded_by points to a different memory than new.supersedes); data corruption from partial updates; manually edited rows.

Common situations: Crash between the two UPDATEs in supersede leaving a half-written edge; a memory being superseded twice so pointers moved to a third node; repair scripts assuming adjacency equals an edge.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/2fbb634527fe422b. Report an issue: GitHub.