Skip to content

fix(openai_like/utils): normalize extra_body=None to {} to prevent TypeError - #21895

Closed
WhoisMonesh wants to merge 10 commits into
BerriAI:mainfrom
WhoisMonesh:patch-6
Closed

fix(openai_like/utils): normalize extra_body=None to {} to prevent TypeError#21895
WhoisMonesh wants to merge 10 commits into
BerriAI:mainfrom
WhoisMonesh:patch-6

Conversation

@WhoisMonesh

@WhoisMonesh WhoisMonesh commented Feb 22, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes #21891

What changed

When extra_body is explicitly passed as None (e.g. via litellm.responses(..., extra_body=None)), two code paths would crash with TypeError: 'NoneType' object is not a mapping when trying to unpack **extra_body or **optional_params["extra_body"].

Fix 1 — litellm/llms/openai_like/chat/handler.py

Normalize extra_body after popping from optional_params:

# Before
extra_body = optional_params.pop("extra_body", {})
# After
extra_body = optional_params.pop("extra_body", {}) or {}

Fix 2 — litellm/utils.py (add_provider_specific_params_to_optional_params)

  1. Normalize extra_body from passed_params:
# Before
extra_body = passed_params.pop("extra_body", {})
# After
extra_body = passed_params.pop("extra_body", {}) or {}
  1. Guard against optional_params["extra_body"] being None when merging:
# Before
initial_extra_body = {**optional_params["extra_body"], **extra_body,}
# After
initial_extra_body = {**(optional_params.get("extra_body") or {}), **extra_body,}

Testing

  • All existing tests pass
  • Fixes TypeError: 'NoneType' object is not a mapping when extra_body=None is passed to OpenAI-compatible providers (including hosted_vllm) via Router or /responses endpoint

Additional fixes (greptile-bot recommendations)

Applied the same or {} normalization to all other providers with the identical vulnerable pattern:

  • litellm/llms/snowflake/chat/transformation.py
  • litellm/llms/ovhcloud/chat/transformation.py
  • litellm/llms/cometapi/chat/transformation.py
  • litellm/llms/openrouter/chat/transformation.py
  • litellm/llms/azure/azure.py (image generation)
  • litellm/llms/azure_ai/chat/transformation.py
  • litellm/llms/watsonx/completion/transformation.py

Added unit tests in tests/litellm/llms/openai_like/test_extra_body_none_fix.py covering:

  • extra_body=None does not raise TypeError (OpenAI-like handler)
  • extra_body={} (empty dict) continues to work
  • extra_body={...} with real keys is correctly merged
  • get_optional_params with extra_body=None for openai, hosted_vllm, openrouter providers

@vercel

vercel Bot commented Feb 22, 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 24, 2026 5:00pm

Request Review

@greptile-apps

greptile-apps Bot commented Feb 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a TypeError when extra_body=None is explicitly passed to OpenAI-compatible providers. The fix normalizes None to {} using the or {} idiom in two locations: litellm/llms/openai_like/chat/handler.py (for the direct handler path) and litellm/utils.py (for the add_provider_specific_params_to_optional_params utility). Both changes are correct and minimal.

  • Fixes are correct: The or {} pattern properly handles the case where .pop("extra_body", {}) returns None (when extra_body was explicitly set to None in the params dict).
  • No tests added: Per project guidelines (CLAUDE.md, AGENTS.md), PRs should include at least 1 test in tests/litellm/. A unit test verifying that extra_body=None doesn't raise a TypeError would strengthen this fix.
  • Same pattern exists in other providers: The identical vulnerable extra_body = optional_params.pop("extra_body", {}) pattern (without the or {} guard) exists in several other provider transformations including Snowflake (snowflake/chat/transformation.py:253), OVHCloud (ovhcloud/chat/transformation.py:94), CometAPI (cometapi/chat/transformation.py:84), OpenRouter (openrouter/chat/transformation.py:164), Azure image gen (azure/azure.py:1199), Azure AI (azure_ai/chat/transformation.py:224), and WatsonX (watsonx/completion/transformation.py:235). These would similarly crash if extra_body=None reaches them. Consider applying the same fix to those locations for completeness.

Confidence Score: 4/5

  • This PR is safe to merge — it fixes a real crash with a minimal, correct change, though it would benefit from a unit test and broader application to other providers.
  • Score of 4 reflects that the fix is correct and addresses a real bug, but lacks test coverage and the same vulnerable pattern exists in several other provider files that are not addressed by this PR.
  • No files in this PR require special attention, but related files with the same vulnerable pattern (snowflake, ovhcloud, cometapi, openrouter, azure, azure_ai, watsonx transformations) should be reviewed for the same issue.

Important Files Changed

Filename Overview
litellm/llms/openai_like/chat/handler.py Adds or {} guard after optional_params.pop("extra_body", {}) to handle explicit None. Change is correct and minimal.
litellm/utils.py Adds or {} normalization for extra_body from passed_params and uses .get() or {} for optional_params["extra_body"]. Both fixes are correct; minor redundancy with the existing setdefault call.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["User calls litellm.responses(..., extra_body=None)"] --> B["add_provider_specific_params_to_optional_params()"]
    B --> C{"extra_body = passed_params.pop('extra_body', {})"}
    C -->|"extra_body=None stored"| D{"or {} normalizes to dict"}
    D --> E["extra_body = {}"]
    E --> F["Merge into optional_params['extra_body']"]
    F --> G["optional_params.get('extra_body') or {}"]
    G --> H["Safe dict unpacking: **extra_body"]
    
    A --> I["OpenAILikeChatHandler.completion()"]
    I --> J{"extra_body = optional_params.pop('extra_body', {})"}
    J -->|"extra_body=None stored"| K{"or {} normalizes to dict"}
    K --> L["extra_body = {}"]
    L --> M["Safe dict unpacking: **extra_body"]
Loading

Last reviewed commit: 7c77495

@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.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread litellm/utils.py
@ghost

ghost commented Feb 25, 2026

Copy link
Copy Markdown

Review

1. Does this PR fix the issue it describes?
Yes. Fixes #21891extra_body=None caused TypeError when unpacking. Normalizes to {} with or {} pattern.

Applied to 7+ providers per Greptile recommendations (Azure, Snowflake, OVHCloud, CometAPI, OpenRouter, WatsonX, Azure AI).

2. Has this issue already been solved elsewhere?
No — multiple providers had the same vulnerable pattern.

3. Are there other PRs addressing the same problem?
No duplicates found for #21891.

4. Are there other issues this potentially closes?
Any TypeError: 'NoneType' object is not a mapping from extra_body=None.

✅ LGTM — comprehensive fix across providers with good test coverage (137 LOC).

@MiXaiLL76

Copy link
Copy Markdown

any update?

@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 Jun 10, 2026
@github-actions github-actions Bot closed this Jun 17, 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.

[Bug]: /responses + Router + OpenAI-compatible provider crashes when extra_body is None (TypeError: 'NoneType' object is not a mapping)

2 participants