BerriAI/litellm · error · ValueError
Invalid completion response: no choices found
Error message
Invalid completion response: no choices found
What it means
When converting a litellm completion ModelResponse back into Google GenAI generate_content format, the translator reads response.choices[0]. An empty choices list (falsy) means there is no candidate to transform — the Google API always returns candidates, so the adapter aborts with ValueError rather than emitting a malformed response.
Source
Thrown at litellm/google_genai/adapters/transformation.py:493
def translate_completion_to_generate_content(
self,
response: ModelResponse,
) -> dict[str, object]:
"""
Transform litellm completion response to Google GenAI generate_content format
Args:
response: ModelResponse from litellm.completion
Returns:
Dict in Google GenAI generate_content response format
"""
# Extract the main response content
choice: Final = response.choices[0] if response.choices else None
if not choice:
raise ValueError("Invalid completion response: no choices found")
# Handle different choice types (Choices vs StreamingChoices)
if isinstance(choice, Choices):
if not choice.message:
raise ValueError("Invalid completion response: no message found in choice")
parts = self._transform_openai_message_to_google_genai_parts(choice.message)
else:
# Fallback for generic choice objects
message_content = getattr(choice, "message", {}).get("content", "") or getattr(choice, "delta", {}).get(
"content", ""
)
parts = [{"text": message_content}] if message_content else []
# Create Google GenAI format response
generate_content_response: Final[dict[str, object]] = {
"candidates": [
{
"content": {"parts": parts, "role": "model"},View on GitHub (pinned to 6c2dcb801b)
Solutions
- Log the raw completion response before the adapter transforms it to confirm choices is empty and why
- Retry without the response-mutating hook/fallback layer to see if choices survive
- Adjust request params (e.g. safety settings, max output tokens) so the provider returns at least one candidate
- Handle the ValueError in your caller and surface a domain-specific 'no candidates' error to the user
Example fix
# before
r = generate_content(model=m, contents=c) # raises when provider returns empty choices
# after
base = litellm.completion(model='gemini/'+m, messages=msgs)
if not base.choices:
raise RuntimeError('provider returned no candidates')
r = generate_content(model=m, contents=c) Defensive patterns
Strategy: validation
Validate before calling
base = litellm.completion(model='gemini/' + model, messages=msgs)
if not getattr(base, "choices", None):
raise RuntimeError("provider returned zero candidates; adjust safety/token params") Type guard
def has_choices(resp) -> bool:
return bool(getattr(resp, "choices", None)) Try / catch
try:
r = generate_content(model=m, contents=c)
except ValueError as e:
if "no choices found" in str(e):
retry_with_relaxed_safety_settings(m, c) Prevention
- Watch for response-mutating hooks that drop choices
- Treat empty candidates as a retryable provider condition
When it happens
Trigger: The underlying completion returned a ModelResponse with choices=[] — possible with some providers on content-filtered/empty completions, error-ish success responses, or middleware (hooks, fallback logic) that strips choices before the transform runs.
Common situations: Safety filters causing providers to return empty candidates; router post-call hooks mutating the response; LiteLLM mock/fake-stream paths that build ModelResponse without choices.
Related errors
- Invalid completion response: no message found in choice
- Failed to transform streaming response
- Error calling litellm.acompletion for generate_content: {e}
- Error calling litellm.completion for generate_content: {e}
- litellm_logging_obj is required, but got None
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/b8a5b93ece6ea68e.
Report an issue: GitHub.