MemPalace/mempalace · error · ValueError

metadatas length {len(metadatas)} does not match ids length

Error message

metadatas length {len(metadatas)} does not match ids length {n}

What it means

Raised by sqlite_exact's `_validate_write_batch`: the optional `metadatas` list was supplied but its length differs from `len(ids)`. Metadatas is optional (None is fine), but once present it must pair 1:1 with ids so each stored row gets the right metadata dict.

Source

Thrown at mempalace/backends/sqlite_exact.py:246

            if not any(_matches_where_document(document, clause) for clause in value or []):
                return False
            continue
        raise UnsupportedFilterError(f"where_document operator {key!r} not supported")
    return True


def _validate_write_batch(
    *,
    documents: list[str],
    ids: list[str],
    metadatas: Optional[list[dict]],
    embeddings: Optional[list[list[float]]],
) -> None:
    n = len(ids)
    if len(documents) != n:
        raise ValueError(f"documents length {len(documents)} does not match ids length {n}")
    if metadatas is not None and len(metadatas) != n:
        raise ValueError(f"metadatas length {len(metadatas)} does not match ids length {n}")
    if embeddings is not None and len(embeddings) != n:
        raise ValueError(f"embeddings length {len(embeddings)} does not match ids length {n}")


class _SQLiteExactHandle:
    def __init__(
        self,
        conn: sqlite3.Connection,
        lock: threading.RLock,
        palace_path: str,
        *,
        read_only: bool = False,
        immutable: bool = False,
    ):
        self.conn = conn
        self.lock = lock
        self.palace_path = palace_path
        self.read_only = read_only

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. If all rows share metadata, repeat it: `metadatas=[meta] * len(ids)`.
  2. Fill missing per-id metadata with `{}` instead of dropping entries: `metadatas = [per_id.get(i, {}) for i in ids]`.
  3. Add a pre-call assert `len(metadatas) == len(ids)` in dev builds and tests.

Example fix

# before
col.upsert(ids=ids, documents=docs, metadatas=[{"wing": "alice"}])  # 1 != N

# after
col.upsert(ids=ids, documents=docs, metadatas=[{"wing": "alice"}] * len(ids))
Defensive patterns

Strategy: validation

Validate before calling

def align_metadatas(ids, metadatas):
    if metadatas is None:
        return None
    if len(metadatas) != len(ids):
        raise ValueError(f"metadatas {len(metadatas)} != ids {len(ids)}")
    return metadatas

Try / catch

try:
    col.upsert(ids=ids, documents=docs, metadatas=metas)
except ValueError as e:
    if "metadatas length" in str(e):
        metas = metas * len(ids) if len(metas) == 1 else [{} for _ in ids]
        col.upsert(ids=ids, documents=docs, metadatas=metas)

Prevention

When it happens

Trigger: Calling upsert/add with `metadatas=[...]` shorter or longer than `ids` — commonly one shared metadata dict passed bare instead of repeated per row, or a comprehension that drops entries where metadata is missing.

Common situations: Passing a single metadata dict when the API expects a list (e.g. `metadatas={"wing": "x"}`); building metadatas with `filter(None, ...)` while ids keep all items; merging metadata from a dict keyed by id where some ids have no entry.

Related errors


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