BerriAI/litellm · error · Exception
/v1/embeddings route returned None Embeddings.
Error message
/v1/embeddings route returned None Embeddings.
What it means
Second half of the async multimodal embedding flow: after a successful /v1/embeddings (text) call, LiteLLM checks response.data and raises '/v1/embeddings route returned None Embeddings.' if the parsed data array is None. Like its image twin, the HTTP call succeeded but no vectors were extracted — usually bad input or a response-shape mismatch.
Source
Thrown at litellm/llms/azure_ai/embed/handler.py:185
if image_embedding_responses is None:
raise Exception("/image/embeddings route returned None Embeddings.")
if v1_embeddings_request["input"]:
response: Final[EmbeddingResponse] = await super().embedding(
model=model,
input=input,
timeout=timeout,
logging_obj=logging_obj,
model_response=model_response,
optional_params=optional_params,
api_key=api_key,
api_base=api_base,
client=client,
aembedding=True,
)
text_embedding_responses = response.data
if text_embedding_responses is None:
raise Exception("/v1/embeddings route returned None Embeddings.")
return self._process_response(
image_embedding_responses=image_embedding_responses,
text_embedding_responses=text_embedding_responses,
image_embeddings_idx=image_embeddings_idx,
model_response=model_response,
input=input,
)
def embedding(
self,
model: str,
input: list,
timeout: float,
logging_obj,
model_response: EmbeddingResponse,
optional_params: dict,
api_key: str | None = None,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Log the input array right before the call — hunt for empty/None/non-string entries and filter them.
- Inspect the raw /v1/embeddings response for one input via curl to see whether data is absent upstream or lost in parsing.
- If raw data exists but litellm yields None, upgrade litellm.
- Send text and images as separate, well-formed lists rather than relying on the combined route to sort them.
Example fix
# before resp = await litellm.aembedding(model='azure_ai/mm-embed', input=texts_and_images) # after texts = [t for t in inputs if isinstance(t, str) and t.strip()] images = [i for i in inputs if is_image(i)] resp = await litellm.aembedding(model='azure_ai/mm-embed', input=texts + images)
Defensive patterns
Strategy: validation
Validate before calling
def clean_text_inputs(inputs: list) -> list[str]:
cleaned = [t for t in inputs if isinstance(t, str) and t.strip()]
if not cleaned:
raise ValueError('no non-empty text inputs for /v1/embeddings')
return cleaned Try / catch
try:
resp = await litellm.aembedding(model='azure_ai/mm-embed', input=clean_text_inputs(texts))
except Exception as e:
if '/v1/embeddings route returned None' in str(e):
logger.error('empty embedding data for inputs=%r', texts)
return fallback_embedding() # or skip batch
raise Prevention
- Filter empty/whitespace chunks in the ingestion pipeline, with a test.
- Send text and images as cleanly separated lists to the combined route.
- Alert whenever data comes back None — it indicates upstream or version drift, not normal behavior.
When it happens
Trigger: aembedding() where the text part of the input is malformed (empty strings, None entries) so Azure returns 200 with no data; or the azure_ai response parser failing on an unexpected payload shape; text-only requests reaching this combined handler with the text list unexpectedly empty-but-truthy.
Common situations: Mixing image and text inputs and a filtering step leaves whitespace-only strings; upstream producer sends dicts where strings were expected; litellm/azure schema drift after preview api-version change.
Related errors
- /image/embeddings route returned None Embeddings.
- Azure client is not an instance of AsyncAzureOpenAI or Async
- Failed to parse raw Azure embedding response: {json_error}
- embedding_response is not an instance of EmbeddingResponse
- azure_client is not an instance of AsyncAzureOpenAI
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/b6f0c04407494ecc.
Report an issue: GitHub.