Symptoms
Loud failure:
pgvector: ERROR: expected 1536 dimensions, not 3072
Azure AI Search: The vector field 'contentVector' ... dimensions mismatch
Qdrant: Wrong input: Vector dimension error: expected dim: 1536, got 3072
Quiet failure (worse): no errors, but results are random-looking. The query and documents were embedded by different models with the same number of dimensions, so the vectors aren't comparable.
Step 1: what changed?
| Check | Where |
|---|---|
| Query embedding config | App config: deployment/model ID, dimensions parameter |
| Ingestion embedding config | Indexer skillset / pipeline config |
| Model deployment | Azure: deployment's model name and version; Bedrock: model ID |
| Recent deploys | App and pipeline release history |
Quick test: embed a known document chunk now, and compare with its stored vector.
import numpy as np
stored = fetch_vector(doc_id="kb-123#4")
fresh = embed(fetch_text(doc_id="kb-123#4"))
cos = np.dot(stored, fresh) / (np.linalg.norm(stored) * np.linalg.norm(fresh))
print(len(stored), len(fresh), round(cos, 4))
Same model and settings → cosine ≈ 1.0 (tiny differences are normal). Much lower → the models differ.
Step 2: restore service
| Situation | Fix |
|---|---|
| Query side changed, index unchanged | Revert the query embedding config to the index's model |
| Ingestion changed, some documents re-embedded with new model | Index is now mixed: roll back to the previous index version, or re-embed everything with one model |
| Deliberate model upgrade done halfway | Point the app back to the old index until the new one is fully built and validated |
| Old model retired by provider | Build a new index with the new model end to end; meanwhile use keyword-only search as a fallback |
Keyword fallback for hybrid-capable stores (Azure AI Search): run the query with text only, no vector query, until vectors are consistent. It's worse than hybrid but much better than random results.
Step 3: rebuild properly (if needed)
- Create a new index/collection with the new dimensions.
- Re-embed all documents with the new model (batch, with rate limits).
- Validate with the labelled question set (recall@k).
- Switch the query embedding config and the index pointer together, in one config change.
See the embedding migration post for the full approach.
Step 4: lock config to the index
Store the embedding settings with the index, and have the app refuse to query with different ones:
INDEX_META = get_index_metadata("kb-v12") # e.g. {"embedding_model": "text-embedding-3-large", "dimensions": 1024}
def embed_query(text):
if (EMBED_CFG.model, EMBED_CFG.dimensions) != (INDEX_META["embedding_model"], INDEX_META["dimensions"]):
raise RuntimeError("Embedding config does not match index metadata. Refusing to query.")
return embed(text, model=EMBED_CFG.model, dimensions=EMBED_CFG.dimensions)
Failing loudly beats silently returning irrelevant results.
Warning
The
dimensionsparameter on models that support shortened embeddings changes the vectors. 1,024-dim and 3,072-dim embeddings from the same model are not interchangeable, and neither are versions of "the same" model from different providers.
Prevent it next time
- Embedding model, version and dimensions are part of the index version, not separate app settings.
- Startup check comparing query config with index metadata.
- Pin embedding deployments' versions; track retirement dates.
- Monitoring on average top-1 similarity score. A sudden drop is a strong signal of mismatch.