run-llama/llama_index · error · ValueError

Must provide either user_msg or chat_history

Error message

Must provide either user_msg or chat_history

What it means

StorageContext.to_dict (used when serializing an entire index to a JSON/dict payload) requires that every store it holds is the in-memory 'simple' implementation: SimpleDocumentStore, SimpleIndexStore, SimpleGraphStore, SimplePropertyGraphStore (or None), and SimpleVectorStore for all vector stores. Any custom or database-backed store makes all_simple False and raises this ValueError, because only the simple stores have a JSON-serializable format.

Source

Thrown at llama-index-core/llama_index/core/agent/workflow/base_agent.py:429

            )
            await ctx.store.set("user_msg_str", content_str)
        elif chat_history and not all(
            message.role == "system" for message in chat_history
        ):
            # If no user message, use the last message from chat history as user_msg_str
            user_hist: List[ChatMessage] = [
                msg for msg in chat_history if msg.role == "user"
            ]
            content_str = "\n".join(
                [
                    block.text
                    for block in user_hist[-1].blocks
                    if isinstance(block, TextBlock)
                ]
            )
            await ctx.store.set("user_msg_str", content_str)
        else:
            raise ValueError("Must provide either user_msg or chat_history")

        # Get all messages from memory
        input_messages = await memory.aget()

        # send to the current agent
        return AgentInput(input=input_messages, current_agent_name=self.name)

    @step
    async def setup_agent(self, ctx: Context, ev: AgentInput) -> AgentSetup:
        """Main agent handling logic."""
        llm_input = [*ev.input]

        if self.system_prompt:
            llm_input = [
                ChatMessage(role="system", content=self.system_prompt),
                *llm_input,
            ]

View on GitHub (pinned to afd0fef371)

Solutions

  1. Persist each store through its own backend instead of to_dict: leave data in the vector DB / docstore and persist only index_struct via index_store, or use index.persist(persist_dir) per-store.
  2. If you need to_dict, construct the StorageContext entirely from simple stores (StorageContext.from_defaults()) and move data into external backends separately.
  3. Serialize only what is serializable: save index structures with SimpleIndexStore and keep documents/vectors in their dedicated stores.
  4. Check isinstance on each store before calling to_dict and raise a clearer domain-specific error in your own code.

Example fix

# before
sc = StorageContext.from_defaults(vector_store=chroma_store)
index.set_index_store(sc.index_store)
payload = sc.to_dict()  # ValueError: to_dict only available when using simple ...

# after
# keep Chroma for vectors; persist only the simple index store
sc = StorageContext.from_defaults()
index = VectorStoreIndex(nodes, storage_context=sc, vector_store=chroma_store)
index.storage_context.index_store.persist("./storage/index_store.json")
Defensive patterns

Strategy: validation

Validate before calling

from llama_index.core.storage.docstore import SimpleDocumentStore
from llama_index.core.storage.index_store import SimpleIndexStore
from llama_index.core.storage.graph_store import SimpleGraphStore
from llama_index.core.vector_stores import SimpleVectorStore

sc = storage_context
ok = (isinstance(sc.docstore, SimpleDocumentStore)
      and isinstance(sc.index_store, SimpleIndexStore)
      and isinstance(sc.graph_store, SimpleGraphStore)
      and all(isinstance(v, SimpleVectorStore) for v in sc.vector_stores.values()))
if ok:
    payload = sc.to_dict()
else:
    payload = None  # persist per-store instead

Type guard

def storage_context_is_simple(sc) -> bool:
    simple = (SimpleDocumentStore, SimpleIndexStore, SimpleGraphStore)
    return (isinstance(sc.docstore, SimpleDocumentStore)
            and isinstance(sc.index_store, SimpleIndexStore)
            and isinstance(sc.graph_store, SimpleGraphStore)
            and all(isinstance(v, SimpleVectorStore) for v in sc.vector_stores.values()))

Try / catch

try:
    payload = sc.to_dict()
except ValueError as e:
    if "simple doc/index/vector stores" in str(e):
        # fall back to per-store persistence
        ...

Prevention

When it happens

Trigger: Building a StorageContext with e.g. ChromaVectorStore, MongoDocumentStore, or RedisKVStore-backed stores and then calling index.storage_context.to_dict() or index.save_to_string()/'index_json' paths that depend on it; calling to_dict after StorageContext.from_defaults(vector_store=custom_store).

Common situations: Starting with default simple stores, later swapping in a production vector DB (Milvus, Qdrant, Weaviate) via storage_context, then still trying index.save_to_dict / save_to_string; attempting to persist the whole context while using PropertyGraphIndex with Neo4j; serialization paths in workflows that assume simple stores.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/ab3fb5660e13575d. Report an issue: GitHub.