langchain-ai/langchain · error · ValueError

IDs must be provided for deletion

Error message

IDs must be provided for deletion

What it means

`ValueError` from the in-memory `DocumentIndex`/`RecordManager` family's `delete`: the `ids` argument is `None`. Deletion requires an explicit list of IDs (there is no 'delete all' via `None`), so the call is rejected rather than interpreted as a no-op or a wipe.

Source

Thrown at libs/core/langchain_core/indexing/in_memory.py:76

        return UpsertResponse(succeeded=ok_ids, failed=[])

    @override
    def delete(self, ids: list[str] | None = None, **kwargs: Any) -> DeleteResponse:
        """Delete by IDs.

        Args:
            ids: List of IDs to delete.

        Raises:
            ValueError: If IDs is None.

        Returns:
            A response object that contains the list of IDs that were successfully
            deleted and the list of IDs that failed to be deleted.
        """
        if ids is None:
            msg = "IDs must be provided for deletion"
            raise ValueError(msg)

        ok_ids = []

        for id_ in ids:
            if id_ in self.store:
                del self.store[id_]
                ok_ids.append(id_)

        return DeleteResponse(
            succeeded=ok_ids, num_deleted=len(ok_ids), num_failed=0, failed=[]
        )

    @override
    def get(self, ids: Sequence[str], /, **kwargs: Any) -> list[Document]:
        return [self.store[id_] for id_ in ids if id_ in self.store]

    @override
    def _get_relevant_documents(

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Guard the call: only delete when you actually have IDs, e.g. `if ids: store.delete(ids)`.
  2. To delete everything in the in-memory index, delete each known ID explicitly (`list(store.store.keys())`) or clear the container you built it over.
  3. Trace where `None` originates (optional dict access, empty query results) and give it a default of `[]`.

Example fix

# before
ids = response.get("ids")  # may be None
store.delete(ids)

# after
ids = response.get("ids") or []
if ids:
    store.delete(ids)
Defensive patterns

Strategy: validation

Validate before calling

ids = ids or []
if ids:
    store.delete(ids)

Type guard

def has_ids_to_delete(ids: list[str] | None) -> bool:
    """Type check plus emptiness guard for delete(ids)."""
    return isinstance(ids, list) and len(ids) > 0 and all(isinstance(i, str) for i in ids)

Try / catch

try:
    store.delete(ids)
except ValueError as e:
    if "IDs must be provided" in str(e):
        pass  # nothing to delete; safe no-op
    else:
        raise

Prevention

When it happens

Trigger: Calling `store.delete(None)` or `await store.adelete(None)` on the in-memory index — commonly when upstream code computes IDs from an empty result and passes the resulting `None`, or when a caller tries `delete()` with no arguments on an API that declares `ids` optional but rejects `None`.

Common situations: `ids = search_results.get('ids')` returning `None` before `delete(ids)`; migrating code from a store where `delete(None)` meant 'delete all'; forgetting to handle an empty-match case in cleanup logic.

Related errors


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