MemPalace/mempalace · error · DimensionMismatchError

sqlite_exact collection

Error message

sqlite_exact collection {self._collection_name!r} expects embedding dimension {expected_dim}, got {int(q.size)}

What it means

Raised during query/search on a sqlite_exact collection: a query embedding's dimension does not match the dimension recorded for the collection. Unlike the write-side checks, this fires per query vector, after rows are loaded — the guard sits inside the query loop right before cosine scoring so the failure names the exact expected vs received sizes.

Solutions

  1. Compute query embeddings with the same model used at ingest — check `col.get_stored_embedder_identity()` and match it.
  2. If you must change models, rebuild/re-ingest the collection (see DimensionMismatchError on write) so both sides share one dimension.
  3. Pre-validate before querying: `assert len(qvec) == expected_dim` where expected_dim comes from the collection metadata.

Example fix

# before
# collection built with 768-dim model
results = col.query(query_embeddings=[embed_large("query")], n_results=5)  # 1536-dim

# after
results = col.query(query_embeddings=[embed_small("query")], n_results=5)  # 768-dim, same model as ingest
Defensive patterns

Strategy: validation

Validate before calling

def validate_query_dim(col, query_embeddings):
    with col._cursor() as cur:
        cid = col._collection_id(cur)
        expected = col._collection_dimension(cur, cid)
    if expected is not None:
        for q in query_embeddings:
            if len(q) != expected:
                raise ValueError(f"query dim {len(q)} != collection dim {expected}; use the ingest embedder")

Try / catch

try:
    col.query(query_embeddings=[qv], n_results=k)
except DimensionMismatchError:
    qv = embed_with_ingest_model(query_text)  # match stored identity
    col.query(query_embeddings=[qv], n_results=k)

Prevention

When it happens

Trigger: Calling query/query_texts with embeddings from a different model than the one that built the collection — e.g. searching a 768-dim palace with 1536-dim query vectors; supplying `query_embeddings` manually computed with the wrong model; a changed default embedder in the searcher between ingest and search.

Common situations: Swapped the local embedding model after building the palace but before searching; a client computes embeddings with a different backend than the ingest pipeline; multiple palaces with different models and the wrong query path taken.

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/0b926e0cf09720a6. Report an issue: GitHub.

Appendix: source

Thrown at mempalace/backends/sqlite_exact.py:612

            raise ValueError("query input must be a non-empty list")

        spec = _IncludeSpec.resolve(include, default_distances=True)
        outer_ids: list[list[str]] = []
        outer_docs: list[list[str]] = []
        outer_metas: list[list[dict]] = []
        outer_dists: list[list[float]] = []
        outer_embeds: list[list[list[float]]] = []

        with self._cursor() as cur:
            collection_id = self._collection_id(cur)
            expected_dim = self._collection_dimension(cur, collection_id)
            rows = self._rows(cur, where=where, where_document=where_document)
            row_vectors = [(row, _decode_array(row["embedding"])) for row in rows]

        for query_vector in query_embeddings:
            q = _as_vector_array(query_vector)
            if expected_dim is not None and int(q.size) != expected_dim:
                raise DimensionMismatchError(
                    f"sqlite_exact collection {self._collection_name!r} expects "
                    f"embedding dimension {expected_dim}, got {int(q.size)}"
                )
            q_norm = float(np.linalg.norm(q))
            scored = []
            for row, vec in row_vectors:
                if vec is None or vec.size != q.size:
                    continue
                denom = q_norm * float(np.linalg.norm(vec))
                cos = 0.0 if denom <= 0 else float(np.dot(q, vec) / denom)
                distance = 1.0 - max(-1.0, min(1.0, cos))
                scored.append((distance, row, vec))
            scored.sort(key=lambda item: item[0])
            top = scored[:n_results]

            outer_ids.append([row["id"] for _, row, _ in top])
            outer_docs.append([row["document"] for _, row, _ in top] if spec.documents else [])
            outer_metas.append([row["metadata"] for _, row, _ in top] if spec.metadatas else [])

View on GitHub (pinned to 06cb6987f0)