Azure OpenAI text-embedding-ada-002 — deprecated, reindex to text-embedding-3
Chat model deprecations are easy — swap the deployment. Embedding model deprecations force a complete reindex of every vector you have ever generated. This page is the migration playbook.
Quick fix (TL;DR)
text-embedding-ada-002 in favour of text-embedding-3-small (better/cheaper) and text-embedding-3-large (higher quality). Vectors from different models are not interchangeable — you must reindex your entire corpus. Fix by (a) choosing v3-small (default) or v3-large (quality-critical), (b) optionally using the new dimensions parameter to downscale, and (c) running the migration behind a dual-write flag before cutting over reads.Real error messages you'll see
These are the exact strings returned by the Azure OpenAI service and its SDKs when this error occurs. Copy-paste-searching any of them should land on this page.
openai.NotFoundError: Error code: 404 - {'error': {'code': 'DeploymentNotFound', 'message': 'The API deployment for this resource does not exist.'}}pinecone.PineconeException: Vector dimension mismatch. Expected 1536, got 3072. (text-embedding-ada-002 = 1536, text-embedding-3-large = 3072 by default)
HTTP/1.1 200 OK x-ms-model-deprecation-notice: "text-embedding-ada-002 will be retired on 2026-06-15. Migrate to text-embedding-3-small (recommended) or text-embedding-3-large."
Reference
Azure OpenAI embedding models — quick comparison
| Model | Default dimensions | MTEB score | Cost per 1M tokens | Recommended for |
|---|---|---|---|---|
text-embedding-ada-002 (deprecated) | 1536 | 61.0 | $0.10 | Legacy — migrate off |
text-embedding-3-small | 1536 (down to 512) | 62.3 | $0.02 | Default choice — cheaper + better |
text-embedding-3-large | 3072 (down to 256) | 64.6 | $0.13 | Quality-critical retrieval, reranking |
Reduced-dimension embeddings — trade-off
v3 models support the dimensions parameter to shorten output vectors while retaining most quality — critical for existing vector DBs with 1536-dim indexes.
| Model | Requested dimensions | MTEB score | Notes |
|---|---|---|---|
| text-embedding-3-small | 1536 (default) | 62.3 | Full quality |
| text-embedding-3-small | 512 | 61.6 | Marginal drop for major storage savings |
| text-embedding-3-large | 3072 (default) | 64.6 | Full quality — most storage |
| text-embedding-3-large | 1536 | 64.1 | Drop-in replacement for ada-002 (same dims) |
| text-embedding-3-large | 256 | 62.0 | 10× smaller, still beats ada-002 |
Root causes, ranked by frequency
Based on developer reports across Azure OpenAI SDK forums, GitHub issues, and Microsoft Q&A during 2025–2026.
- 34%Application still targeting ada-002 deployment. Team assumed embedding models retire on the same slow cycle as chat models. ada-002 has a firm retirement date.
- 20%Dimension mismatch on read after partial migration. Half the corpus is reindexed to v3-large (3072), half is still ada-002 (1536). Vector DB rejects queries or returns wrong results.
- 14%Reindex not budgeted. Team plans model swap but forgets that every existing vector must be recomputed — could be billions of vectors, tens of thousands of dollars.
- 10%No dual-write during migration. Cutover to new model without keeping the old index warm — one bug and you have no working retrieval.
- 8%Downstream reranker trained on ada-002 embeddings. Distributions differ across models; rerankers, classifiers, and RAG scoring pipelines behave differently on v3 embeddings.
- 6%Vector DB schema hardcoded to 1536. Pinecone, Weaviate, pgvector, Qdrant all have per-index dimension. New model with different dimensions requires a new index.
- 5%Batch API used for reindex without dedup. Same source documents embedded multiple times, doubling cost.
- 3%Cross-model similarity assumed transitive. Comparing ada-002 vectors against v3 vectors yields nonsense — cosine similarity does not translate across models.
Fixes — copy-paste solutions
Choose v3-small or v3-large based on your quality target
Start with text-embedding-3-small unless you have measured that v3-large produces meaningfully better retrieval on your evaluation set. For most RAG workloads, v3-small is the correct choice.
"""Evaluation harness — measure v3-small vs v3-large on your data. Run this ONCE with a representative sample of queries + labeled relevant docs before committing to a model choice. """ import os from openai import AzureOpenAI from typing import List client = AzureOpenAI(api_key="...", azure_endpoint="...", api_version="2024-10-21") def embed(text: str, deployment: str, dimensions: int = None) -> List[float]: kwargs = {"model": deployment, "input": text} if dimensions: kwargs["dimensions"] = dimensions response = client.embeddings.create(**kwargs) return response.data[0].embedding def cosine(a: List[float], b: List[float]) -> float: import math dot = sum(x * y for x, y in zip(a, b)) na = math.sqrt(sum(x * x for x in a)) nb = math.sqrt(sum(x * x for x in b)) return dot / (na * nb) def recall_at_k(queries, docs, deployment, k=10, dimensions=None): """queries: [(query_text, relevant_doc_ids)]; docs: {doc_id: text}""" doc_embs = {i: embed(t, deployment, dimensions) for i, t in docs.items()} hits = 0 for query, relevant in queries: qe = embed(query, deployment, dimensions) scored = sorted(doc_embs.items(), key=lambda x: cosine(qe, x[1]), reverse=True)[:k] if any(doc_id in relevant for doc_id, _ in scored): hits += 1 return hits / len(queries) # Build eval set from your production data (100-1000 labeled queries is enough) queries = [...] # [(query, {relevant_doc_ids})] docs = {...} # {doc_id: doc_text} for cfg in [ ("text-embedding-3-small", None), ("text-embedding-3-small", 512), ("text-embedding-3-large", 1536), ("text-embedding-3-large", None), # 3072 ]: deployment, dims = cfg score = recall_at_k(queries, docs, deployment, k=10, dimensions=dims) print(f"{deployment} ({dims or 'default'}): recall@10 = {score:.3f}")
Reindex with dual-write to avoid downtime
Migrate in three phases: (1) dual-write to both old and new indexes; (2) run the full backfill of historical data; (3) shift reads to the new index; (4) decommission the old. This bounds the risk of a bad model choice.
"""Dual-write migration from ada-002 to text-embedding-3-large.""" import os from openai import AzureOpenAI client = AzureOpenAI(api_key="...", azure_endpoint="...", api_version="2024-10-21") # Phase flags — controlled via env or feature flag service DUAL_WRITE = os.getenv("EMBEDDINGS_DUAL_WRITE") == "true" READ_FROM_NEW = os.getenv("EMBEDDINGS_READ_FROM_NEW") == "true" # Deployments DEPLOYMENT_OLD = "ada002-legacy" # to be retired DEPLOYMENT_NEW = "text-embedding-3-large" # Vector DBs (simplified — pinecone, weaviate, etc.) index_old = ... # 1536-dim index_new = ... # 3072-dim (or 1536 if you use dimensions=1536 on 3-large) def embed(text: str, deployment: str, dimensions: int = None): kwargs = {"model": deployment, "input": text} if dimensions: kwargs["dimensions"] = dimensions return client.embeddings.create(**kwargs).data[0].embedding def upsert_document(doc_id: str, text: str): """Called at write time. In dual-write, writes to both indexes.""" # Always write to the new index emb_new = embed(text, DEPLOYMENT_NEW) # 3072-dim index_new.upsert(doc_id, emb_new, metadata={"text": text}) if DUAL_WRITE: emb_old = embed(text, DEPLOYMENT_OLD) index_old.upsert(doc_id, emb_old, metadata={"text": text}) def search(query: str, k: int = 10): """Called at read time. Dispatches to the active index.""" if READ_FROM_NEW: qe = embed(query, DEPLOYMENT_NEW) return index_new.query(qe, top_k=k) else: qe = embed(query, DEPLOYMENT_OLD) return index_old.query(qe, top_k=k) # Backfill script — run once per historical batch def backfill_batch(doc_batch): """Backfill new index from historical corpus.""" texts = [d["text"] for d in doc_batch] response = client.embeddings.create(model=DEPLOYMENT_NEW, input=texts) for doc, emb in zip(doc_batch, response.data): index_new.upsert(doc["id"], emb.embedding, metadata=doc.get("metadata", {}))
Use dimensions parameter to keep 1536-dim index
If your vector database index is hardcoded to 1536 dimensions and re-indexing to 3072 is not practical, use v3-large with dimensions=1536. You get better quality than ada-002 without changing your index schema.
from openai import AzureOpenAI client = AzureOpenAI(api_key="...", azure_endpoint="...", api_version="2024-10-21") # text-embedding-3-large truncated to 1536 dims # Beats ada-002 quality, same dimensions, same index schema def embed_v3_dropin(texts: list) -> list: response = client.embeddings.create( model="text-embedding-3-large", # your deployment name for 3-large input=texts, dimensions=1536, # match your existing index dimensionality ) return [d.embedding for d in response.data] # Verify quality parity before switching sample_texts = ["A dog barks", "A canine vocalises loudly"] old_vecs = ... # from ada-002 new_vecs = embed_v3_dropin(sample_texts) # Cosine similarity between semantically similar sentences should be higher on v3 print("v3-large @1536:", cosine(new_vecs[0], new_vecs[1])) print("ada-002: ", cosine(old_vecs[0], old_vecs[1]))
Prevention checklist
Ship these seven safeguards once and this error stops appearing in your logs.
- Track the retirement date for every embedding model in production — put it in Ops calendars.
- Never mix embeddings from different models in the same vector database index.
- Use Batch API for reindexing when latency does not matter — 50% cost saving over real-time.
- Version your vector index name (
docs-v1,docs-v2) to enable side-by-side migration. - Log the model + dimensions on every write to your vector DB metadata — you will thank yourself during migration.
- Evaluate v3-small first — it beats ada-002 at 1/5th the cost for most use cases.
- Downstream classifiers or rerankers on top of embeddings must be re-trained after a model change — they are model-specific.
Frequently asked questions
Related errors & hubs
Get the weekly AI-error digest
New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.