langchain-ai/langchain · error · ValueError

Length of keys must match length of group_ids

Error message

Length of keys must match length of group_ids

What it means

`ValueError` from `RecordManager.update` (and mirrored in `aupdate`): the `keys` sequence and the optional `group_ids` sequence have different lengths. Each key must be paired with exactly one group ID, so a mismatch means the caller's data is malformed and the upsert is rejected before any record is written.

Source

Thrown at libs/core/langchain_core/indexing/base.py:298

            group_ids: A list of group IDs corresponding to the keys.

            time_at_least: Optional timestamp. Implementation can use this
                to optionally verify that the timestamp IS at least this time
                in the system that stores.
                E.g., use to validate that the time in the postgres database
                is equal to or larger than the given timestamp, if not
                raise an error.
                This is meant to help prevent time-drift issues since
                time may not be monotonically increasing!

        Raises:
            ValueError: If the length of keys doesn't match the length of group
                ids.
            ValueError: If time_at_least is in the future.
        """
        if group_ids and len(keys) != len(group_ids):
            msg = "Length of keys must match length of group_ids"
            raise ValueError(msg)
        for index, key in enumerate(keys):
            group_id = group_ids[index] if group_ids else None
            if time_at_least and time_at_least > self.get_time():
                msg = "time_at_least must be in the past"
                raise ValueError(msg)
            self.records[key] = {"group_id": group_id, "updated_at": self.get_time()}

    async def aupdate(
        self,
        keys: Sequence[str],
        *,
        group_ids: Sequence[str | None] | None = None,
        time_at_least: float | None = None,
    ) -> None:
        """Async upsert records into the database.

        Args:
            keys: A list of record keys to upsert.

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Make group IDs line up 1:1: `group_ids=[group_for(k) for k in keys]`.
  2. For a shared group across all keys: `group_ids=[group] * len(keys)`.
  3. Add a guard before the call: `if group_ids is not None: assert len(keys) == len(group_ids)`.

Example fix

# before
record_manager.update(["k1", "k2", "k3"], group_ids=["g1", "g2"])

# after
record_manager.update(["k1", "k2", "k3"], group_ids=["g1", "g2", "g3"])
Defensive patterns

Strategy: validation

Validate before calling

if group_ids is not None:
    assert len(keys) == len(group_ids), f"{len(keys)} keys vs {len(group_ids)} group_ids"

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling `record_manager.update(keys, group_ids=group_ids)` where `len(keys) != len(group_ids)` — e.g. computing group IDs for a filtered subset of keys, or passing a single group ID string instead of a one-element list when updating one key.

Common situations: Custom RecordManager callers (the built-in indexer always passes matched lengths); passing `group_ids=["g"]` with `keys=["a","b"]`; list comprehensions where the group-ids comprehension filters or drops entries.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/a9bfdb828e8b2a37. Report an issue: GitHub.