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 = None

View on GitHub (pinned to 322425c43b)

Solutions

  1. Verify both IDs exist (SELECT) before invoking detach_supersession.
  2. Regenerate edge pairs from the current database state (superseded_by/supersedes columns) rather than cached lists.
  3. 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

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


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