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

  1. Log the raw completion response before the adapter transforms it to confirm choices is empty and why
  2. Retry without the response-mutating hook/fallback layer to see if choices survive
  3. Adjust request params (e.g. safety settings, max output tokens) so the provider returns at least one candidate
  4. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/b8a5b93ece6ea68e. Report an issue: GitHub.