LlamaIndex Settings — global vs per-object LLM and embed_model
<code>Settings</code> is LlamaIndex's global config singleton. It is convenient in scripts and a source of confusion in production apps where different indexes or requests want different models.
Quick fix (TL;DR)
Settings.llm and Settings.embed_model are global defaults. They are used when you do not pass explicit llm= or embed_model= to a specific component. Fix by (a) always passing explicit models to critical components, (b) never mutating Settings mid-request in a multi-tenant app, and (c) treating Settings as an application-wide default only.Real error messages you'll see
These are the exact strings returned by the LlamaIndex framework and its integrations when this error occurs. Copy-paste-searching any of them should land on this page.
# Request 1: sets Settings.llm = OpenAI(gpt-4o) # Request 2 concurrent: sets Settings.llm = Anthropic # Request 1 continues; ends up calling Anthropic on a query it started with OpenAI
# Background task built index with Settings.embed_model = X # Main thread later sets Settings.embed_model = Y # Query uses Y; retrieval broken
# Worker A ingests with default OpenAI embed # Worker B ingests with an override; Settings.embed_model was changed # Same index; two embedding spaces mixed
Reference
What Settings holds and how it is used
| Setting | Purpose | Default |
|---|---|---|
Settings.llm | Default LLM for chat / completion | OpenAI (env-var API key) |
Settings.embed_model | Default embedding model | OpenAI text-embedding-ada-002 |
Settings.node_parser | Default node parser | SentenceSplitter (chunk=1024) |
Settings.callback_manager | Default callback handler | Empty |
Settings.chunk_size | Default chunk_size for node parser | 1024 |
Settings.chunk_overlap | Default chunk_overlap | 20 |
Root causes, ranked by frequency
Based on developer reports across LlamaIndex forums, GitHub issues, and Discord community during 2025–2026.
- 25%Concurrent requests mutating Settings. Two requests overwrite each other's config.
- 18%Settings changed after object created. Index or engine captured old Settings; later mutation ignored.
- 14%Different modules setting different defaults. Import order determines which wins.
- 10%Test setup pollutes production Settings. Test suite mutates Settings; not reset.
- 8%Missing embed_model at load time. load_index_from_storage uses whatever Settings says; not verified.
- 7%Global side effects in libraries. Imported library mutates Settings on import.
- 7%Multi-tenant config leakage. Tenant A's LLM used for tenant B.
- 11%Async context confusion. Settings is not context-var-scoped; async tasks share it.
Fixes — copy-paste solutions
Always pass explicit models to critical components
For any index, query engine, agent, or chain that matters, pass llm= and embed_model= explicitly. Reserve Settings for convenience in scripts and prototypes.
from llama_index.core import VectorStoreIndex, Settings from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.llms.openai import OpenAI from llama_index.llms.anthropic import Anthropic # --- Bad: relies on global Settings --- # Settings.llm = OpenAI(model="gpt-4o") # Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small") # index = VectorStoreIndex.from_documents(docs) # query_engine = index.as_query_engine() # --- Good: explicit models --- embed_model = OpenAIEmbedding(model="text-embedding-3-small") main_llm = OpenAI(model="gpt-4o-mini") index = VectorStoreIndex.from_documents( docs, embed_model=embed_model, # ← explicit ) # Use different LLMs for different query engines fast_engine = index.as_query_engine( llm=OpenAI(model="gpt-4o-mini"), embed_model=embed_model, similarity_top_k=6, ) quality_engine = index.as_query_engine( llm=OpenAI(model="gpt-4o"), embed_model=embed_model, similarity_top_k=10, response_mode="tree_summarize", ) # Per-tenant / per-request LLM def get_engine_for_tenant(tenant_id: str, index): tenant_llm = load_tenant_llm(tenant_id) # your logic return index.as_query_engine( llm=tenant_llm, embed_model=embed_model, similarity_top_k=6, ) # Multi-model comparison alt_engine = index.as_query_engine( llm=Anthropic(model="claude-sonnet-5"), embed_model=embed_model, )
Never mutate Settings mid-request in a multi-tenant app
For multi-tenant apps, treat Settings as immutable after startup. Per-request config lives in the request handler, not in global state.
from fastapi import FastAPI from llama_index.core import Settings from llama_index.llms.openai import OpenAI from llama_index.embeddings.openai import OpenAIEmbedding app = FastAPI() # --- App startup: set default Settings ONCE --- @app.on_event("startup") async def init_settings(): Settings.llm = OpenAI(model="gpt-4o-mini") Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small") # Never mutated again after this # --- Per-tenant config resolved per-request --- def get_tenant_llm(tenant_id: str): # Look up tenant-specific LLM (API key, model choice) return OpenAI( api_key=fetch_tenant_key(tenant_id), model=fetch_tenant_model(tenant_id, default="gpt-4o-mini"), ) @app.post("/chat/{tenant_id}") async def chat(tenant_id: str, message: str): # DO NOT: Settings.llm = get_tenant_llm(tenant_id) ← global mutation # DO: use per-request llm explicitly tenant_llm = get_tenant_llm(tenant_id) tenant_index = load_tenant_index(tenant_id) query_engine = tenant_index.as_query_engine(llm=tenant_llm) return {"response": str(query_engine.query(message))} # --- Multi-worker gunicorn / uvicorn --- # Each worker has its OWN Settings singleton — so worker-level Settings.llm # is fine, but do not mutate at request time.
Reset Settings between tests
Pytest fixtures should reset Settings state to a known baseline. Otherwise, a test that mutates Settings breaks later tests in ways depending on order.
# conftest.py — reset Settings state between tests import pytest from llama_index.core import Settings from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.llms.openai import OpenAI @pytest.fixture(autouse=True) def reset_llamaindex_settings(): """Snapshot Settings before each test, restore after.""" snapshot = { "llm": Settings.llm, "embed_model": Settings.embed_model, "node_parser": Settings.node_parser, "chunk_size": Settings.chunk_size, "chunk_overlap": Settings.chunk_overlap, } try: yield finally: Settings.llm = snapshot["llm"] Settings.embed_model = snapshot["embed_model"] Settings.node_parser = snapshot["node_parser"] Settings.chunk_size = snapshot["chunk_size"] Settings.chunk_overlap = snapshot["chunk_overlap"] # In a specific test that needs a custom LLM def test_custom_llm(reset_llamaindex_settings): Settings.llm = OpenAI(model="gpt-3.5-turbo") # ... test logic ... # After the test, Settings is restored automatically # For unit tests, prefer FakeLLM and MockEmbedding from llama_index.core.llms.mock import MockLLM from llama_index.core.embeddings.mock_embed_model import MockEmbedding def test_query_engine_logic(): mock_llm = MockLLM(max_tokens=100) mock_embed = MockEmbedding(embed_dim=1536) # Pass explicitly — never rely on Settings in tests index = VectorStoreIndex.from_documents( docs, llm=mock_llm, embed_model=mock_embed, ) query_engine = index.as_query_engine(llm=mock_llm, embed_model=mock_embed) response = query_engine.query("test") assert response
Prevention checklist
Ship these seven safeguards once and this error stops appearing in your logs.
- Pass
llm=andembed_model=explicitly to every index, query engine, and agent. - Set
Settingsonce at app startup and never mutate at request time. - For multi-tenant apps, resolve tenant-specific config per-request; do not put it in Settings.
- Reset Settings between tests via a pytest fixture.
- For async apps, be aware Settings is not context-var-scoped.
- Avoid library imports that mutate Settings; audit third-party imports.
- Use
MockLLMandMockEmbeddingin unit tests to avoid provider calls.
Frequently asked questions
Settings for global defaults, or pass models explicitly to components. Explicit passing is preferred for production.Settings.embed_model. Common causes: a library imported in between, a test setup, or code that dynamically swaps models. Always be explicit.Related errors & hubs
Get the weekly AI-error digest
New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.