Skip to content

fix: handle empty string finish_reason in API responses - #20732

Closed
0xnxxh wants to merge 1 commit into
BerriAI:mainfrom
0xnxxh:fix-empty-finish-reason
Closed

fix: handle empty string finish_reason in API responses#20732
0xnxxh wants to merge 1 commit into
BerriAI:mainfrom
0xnxxh:fix-empty-finish-reason

Conversation

@0xnxxh

@0xnxxh 0xnxxh commented Feb 9, 2026

Copy link
Copy Markdown

Problem

Some OpenAI-compatible API providers (e.g., packyapi) return an empty string "" for the finish_reason field instead of a valid value like "stop". This causes pydantic validation errors when litellm tries to parse the response.

Error

pydantic_core._pydantic_core.ValidationError: 1 validation error for Choices
finish_reason
  Input should be 'stop', 'content_filter', 'function_call', 'tool_calls',
  'length', 'guardrail_intervened', 'eos', 'finish_reason_unspecified' or
  'malformed_function_call'

Solution

Changed the condition from if finish_reason is None: to if not finish_reason: in convert_dict_to_response.py to handle both None and empty string cases, defaulting to "stop" when finish_reason is falsy.

Testing

Tested with packyapi which returns empty string for finish_reason. After this fix, the response is parsed successfully with finish_reason defaulting to "stop".

Changes

  • Modified litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py line 591
  • Changed if finish_reason is None: to if not finish_reason: # Handle both None and empty string

This is a minimal, backward-compatible fix that improves compatibility with OpenAI-compatible API providers that don't strictly follow the spec.

Some OpenAI-compatible API providers (e.g., packyapi) return an empty
string "" for the finish_reason field instead of a valid value like
"stop". This causes pydantic validation errors when litellm tries to
parse the response.

This fix changes the condition from `if finish_reason is None:` to
`if not finish_reason:` to handle both None and empty string cases,
defaulting to "stop" when finish_reason is falsy.

Fixes the following error:
pydantic_core._pydantic_core.ValidationError: 1 validation error for Choices
finish_reason
  Input should be 'stop', 'content_filter', 'function_call', 'tool_calls',
  'length', 'guardrail_intervened', 'eos', 'finish_reason_unspecified' or
  'malformed_function_call'
@vercel

vercel Bot commented Feb 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 9, 2026 3:26am

Request Review

@CLAassistant

CLAassistant commented Feb 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@greptile-apps

greptile-apps Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

Changed finish_reason validation from if finish_reason is None: to if not finish_reason: in convert_to_model_response_object() to handle OpenAI-compatible API providers that return empty strings instead of None.

  • Fixed pydantic validation errors when providers like packyapi return "" for finish_reason
  • Empty strings now default to "stop" like None values
  • Two other similar patterns in the file (lines 144-145 and 195-197) need the same fix for consistency

Confidence Score: 3/5

  • This PR is safe to merge but incomplete - it addresses the issue in one location while two other similar patterns remain unfixed
  • The fix correctly handles empty string finish_reason values and is backward-compatible (both None and empty strings are falsy in Python). However, the same issue exists in two other functions (convert_to_streaming_response at line 195 and an async generator at line 144) that also check if finish_reason is None: and will fail with empty strings. The fix is correct but incomplete.
  • The same file needs attention at lines 144-145 and 195-197 where identical finish_reason checks exist

Important Files Changed

Filename Overview
litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py Changed finish_reason check from is None to not finish_reason to handle empty strings, but two other similar patterns in the file need the same fix

Sequence Diagram

sequenceDiagram
    participant Provider as API Provider
    participant LiteLLM as LiteLLM
    participant Parser as convert_to_model_response_object
    participant Validator as Pydantic Model
    
    Provider->>LiteLLM: API Response with finish_reason=""
    LiteLLM->>Parser: response_object
    Parser->>Parser: choice.get("finish_reason", None)
    Note over Parser: Returns "" (empty string)
    Parser->>Parser: if not finish_reason
    Note over Parser: Evaluates to True (empty string is falsy)
    Parser->>Parser: finish_reason = "stop"
    Parser->>Validator: Choices(finish_reason="stop", ...)
    Validator->>LiteLLM: Valid ModelResponse
    LiteLLM->>Provider: Success
Loading

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps

greptile-apps Bot commented Feb 9, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (2)

litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py
same issue exists here - empty string finish_reason won't be caught by is None check

        if not finish_reason:
            finish_reason = choice.get("finish_details")

litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py
same issue exists here - empty string finish_reason won't be caught by is None check

        if not finish_reason:
            # gpt-4 vision can return 'finish_reason' or 'finish_details'
            finish_reason = choice.get("finish_details")

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@github-actions github-actions Bot added the stale label May 11, 2026
@github-actions github-actions Bot closed this May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants