headroomlabs-ai/headroom · error · ValueError

Memory {memory.id} has no embedding

Error message

Memory {memory.id} has no embedding

What it means

Raised by SQLiteVectorIndex._prepare_memory_for_index when a Memory passed for indexing has embedding None. Like the HNSW backend, the sqlite-vec index only stores pre-computed vectors and expects embedding to be produced by an external Embedder first.

Source

Thrown at headroom/memory/adapters/sqlite_vector.py:296

        conn: sqlite3.Connection,
        memory_ids: list[str],
    ) -> dict[str, int]:
        """Fetch rowids for the given memory IDs."""
        rowids: dict[str, int] = {}
        for chunk in self._chunked(memory_ids):
            placeholders = ", ".join("?" for _ in chunk)
            rows = conn.execute(
                f"SELECT rowid, memory_id FROM vec_metadata WHERE memory_id IN ({placeholders})",
                chunk,
            ).fetchall()
            for row in rows:
                rowids[str(row["memory_id"])] = int(row["rowid"])
        return rowids

    def _prepare_memory_for_index(self, memory: Memory) -> tuple[np.ndarray, VectorMetadata]:
        """Validate a memory and prepare it for indexing."""
        if memory.embedding is None:
            raise ValueError(f"Memory {memory.id} has no embedding")

        embedding = np.asarray(memory.embedding, dtype=np.float32)
        if embedding.shape[0] != self._dimension:
            raise ValueError(
                f"Embedding dimension {embedding.shape[0]} does not match "
                f"index dimension {self._dimension}"
            )

        return embedding, VectorMetadata.from_memory(memory)

    def _metadata_insert_params(self, memory_id: str, metadata: VectorMetadata) -> tuple[Any, ...]:
        """Build INSERT parameters for vector metadata."""
        return (
            memory_id,
            metadata.user_id,
            metadata.session_id,
            metadata.agent_id,
            metadata.importance,

View on GitHub (pinned to 322425c43b)

Solutions

  1. Embed the memory before indexing: memory.embedding = await embedder.embed(memory.content).
  2. Filter or re-queue memories with None embeddings in batch indexing loops.
  3. Make embedding mandatory in your ingest path so un-embedded memories cannot reach the index.

Example fix

// before
await index.index_memory(memory)  # embedding is None

// after
if memory.embedding is None:
    memory.embedding = await embedder.embed(memory.content)
await index.index_memory(memory)
Defensive patterns

Strategy: validation

Validate before calling

if memory.embedding is None:
    memory.embedding = await embedder.embed(memory.content)
await index.index_memory(memory)

Type guard

def has_embedding(m: Memory) -> bool:
    return m.embedding is not None

Try / catch

try:
    await index.index_memory(memory)
except ValueError as e:
    if "no embedding" in str(e):
        memory.embedding = await embedder.embed(memory.content)
        await index.index_memory(memory)
    else:
        raise

Prevention

When it happens

Trigger: Calling index_memory/add with a memory that skipped the embedding step; embeddings computed asynchronously and not awaited; memories loaded from storage where the embedding column was null.

Common situations: Pipeline order bugs (index before embed); optional embeddings in the schema left unset; batch jobs where some records failed embedding earlier.

Related errors


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