crewAIInc/crewAI · warning · ValueError

No documents were inserted.

Error message

No documents were inserted.

What it means

upsert() writes documents with bulk_replace_one(..., upsert=True) and then checks result.upserted_ids. If upserted_ids is None/empty, it raises ValueError('No documents were inserted.') — meaning Mongo acknowledged the bulk_write but reported no upserts. In practice this happens when every operation matched an existing _id (updates, not inserts), or when the input arrays were empty so no operations were built.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/mongodb_vector_search_tool/vector_search.py:265

        if not texts:
            return []
        # Compute embedding vectors
        embeddings = self._embed_texts(texts)
        docs = [
            {
                "_id": ObjectId(i),
                self.text_key: t,
                self.embedding_key: embedding,
                **m,
            }
            for i, t, m, embedding in zip(
                ids, texts, metadatas, embeddings, strict=False
            )
        ]
        operations = [ReplaceOne({"_id": doc["_id"]}, doc, upsert=True) for doc in docs]
        result = self._coll.bulk_write(operations)
        if result.upserted_ids is None:
            raise ValueError("No documents were inserted.")
        return [str(_id) for _id in result.upserted_ids.values()]

    def _run(self, query: str) -> str:
        from bson import json_util

        try:
            query_config = self.query_config or MongoDBVectorSearchConfig()
            limit = query_config.limit
            oversampling_factor = query_config.oversampling_factor
            pre_filter = query_config.pre_filter
            include_embeddings = query_config.include_embeddings
            post_filter_pipeline = query_config.post_filter_pipeline

            query_vector = self._embed_texts([query])[0]

            # Atlas Vector Search, potentially with filter
            stage = {
                "index": self.vector_index_name,

View on GitHub (pinned to 754d7323be)

Solutions

  1. If re-ingesting existing ids, treat this as expected: catch the ValueError or check matched_count instead of upserted_ids semantics
  2. Validate inputs are non-empty and equal-length before calling: assert len(ids) == len(texts) and ids
  3. For genuinely new data, confirm you are not reusing ObjectIds from a previous run — generate fresh ones or drop the collection first

Example fix

# before
returned = tool.upsert(texts=docs, ids=existing_ids)  # ValueError on re-run

# after
# treat existing ids as update, not error
ids = [str(ObjectId()) for _ in docs]  # or verify len(inputs) > 0 first
assert docs, "nothing to upsert"
returned = tool.upsert(texts=docs, ids=ids)
Defensive patterns

Strategy: validation

Validate before calling

def upsert_inputs_valid(texts, ids, metadatas=None) -> bool:
    return bool(texts) and bool(ids) and len(texts) == len(ids) and (
        metadatas is None or len(metadatas) == len(texts)
    )

Try / catch

try:
    tool.upsert(texts=texts, ids=ids)
except ValueError as e:
    if "No documents were inserted" in str(e):
        # ids already existed — treat as idempotent update, not a failure
        pass
    else:
        raise

Prevention

When it happens

Trigger: Calling upsert with ids that already exist in the collection (ReplaceOne with upsert=True updates in place — no upserted ids); passing empty ids/texts lists; strict=False zip silently truncating mismatched-length inputs to nothing; duplicate ids in one batch.

Common situations: Re-ingesting the same corpus expecting new inserts; empty first batch from a chunker bug; ids list length != texts length so zip yields fewer/zero pairs.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/83124968eccadca8. Report an issue: GitHub.