LiteLLM is a common open-source LLM gateway. Teams use it for unified routing, multiple API keys, usage visibility, and fallback policies. When Claude API sits behind LiteLLM, most failures fall into four categories:
- headers are overwritten or dropped
- streaming breaks or returns malformed chunks
- model routing points to the wrong protocol or backend
- MCP OAuth sessions lose state in multi-instance deployments
This guide is based on LiteLLM public documentation, community issue patterns, and common gateway-compatibility problems. LiteLLM behavior changes across versions, so treat every configuration detail here as something to verify against your installed version.
Start with the troubleshooting order
Do not begin by changing application code. Debug the layers in order.

1. Test ClaudeAPI directly
↓
2. Test the same request through LiteLLM Proxy
↓
3. Enable LiteLLM DEBUG logs and compare inbound/outbound headers and body
↓
4. Check client SDK timeout, model name, and stream parsing
↓
5. Check deployment state: multi-instance sessions, load balancing, shared storage
1. Test ClaudeAPI directly
↓
2. Test the same request through LiteLLM Proxy
↓
3. Enable LiteLLM DEBUG logs and compare inbound/outbound headers and body
↓
4. Check client SDK timeout, model name, and stream parsing
↓
5. Check deployment state: multi-instance sessions, load balancing, shared storage
This avoids a common mistake: seeing 400, 404, or stream interrupted and immediately rewriting business logic. Often, the backend Claude API path is healthy and the problem is in gateway conversion, header forwarding, timeout handling, or deployment state.
Minimal diagnosis matrix
| Test path | Result | Likely conclusion |
|---|---|---|
Direct https://gw.claudeapi.com works, LiteLLM fails |
Gateway-layer issue | Check litellm_config.yaml, headers, provider prefix |
| Direct request also fails | Backend request or key issue | Check API key, model ID, request body, rate limits |
| Non-streaming works, streaming fails | Long connection or SSE issue | Check stream_timeout, proxy buffering, client parsing |
| Single instance works, multi-instance fails | State-sharing issue | Check Redis/session store, OAuth callback routing |
Basic LiteLLM + Claude API architecture
Typical path:
Your app -> LiteLLM Proxy -> ClaudeAPI gateway -> Claude model
Your app -> LiteLLM Proxy -> ClaudeAPI gateway -> Claude model
LiteLLM usually handles:
- protocol conversion between OpenAI-style and provider-native requests
- header passthrough and rewriting
- model routing
- fallback and retries
- optional session or gateway behavior for longer workflows

If any of those layers disagrees with what Claude API expects, you can see confusing errors.
Minimal configuration reference
This is a reference shape, not the only correct configuration:
# litellm_config.yaml
model_list:
- model_name: claude-sonnet-4-6
litellm_params:
model: anthropic/claude-sonnet-4-6
api_key: os.environ/CLAUDE_API_KEY
api_base: https://gw.claudeapi.com
# litellm_config.yaml
model_list:
- model_name: claude-sonnet-4-6
litellm_params:
model: anthropic/claude-sonnet-4-6
api_key: os.environ/CLAUDE_API_KEY
api_base: https://gw.claudeapi.com
If you use the direct Anthropic endpoint, the base URL would be Anthropic’s endpoint. This guide uses ClaudeAPI’s gateway:
https://gw.claudeapi.com
https://gw.claudeapi.com
Do not mix Anthropic-native and OpenAI-compatible paths
LiteLLM provider prefix and api_base must match the protocol.
| Target protocol | LiteLLM provider | api_base |
Backend request style |
|---|---|---|---|
| Anthropic Messages API | anthropic/claude-sonnet-5 |
https://gw.claudeapi.com |
/v1/messages |
| OpenAI-compatible Chat Completions | openai/claude-sonnet-5 |
https://gw.claudeapi.com/v1 |
/v1/chat/completions |
Common broken combinations:
model: anthropic/...withapi_baseending in/v1model: openai/...with an Anthropic Messages request body- client sends
model: claude-sonnet-5, but LiteLLM only registered another alias
These often appear as:
400 Bad Request404 model not found- malformed streaming events
- provider-specific validation errors
Problem 1: anthropic-beta header is overwritten or dropped
Symptoms
- beta features return
400 - extended thinking, PDF parsing, or another beta behavior does not activate
- direct backend requests work, but LiteLLM requests do not
Why it happens
When LiteLLM converts between protocols or reconstructs provider requests, headers may be filtered, overwritten, or not forwarded as expected. This is especially relevant for headers such as:
anthropic-beta
anthropic-version
x-api-key
anthropic-beta
anthropic-version
x-api-key
The exact behavior depends on LiteLLM version and provider implementation.
Step 1: confirm the direct request works
curl https://gw.claudeapi.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_CLAUDEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: extended-thinking-2025-01-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "test"}]
}'
curl https://gw.claudeapi.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_CLAUDEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: extended-thinking-2025-01-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "test"}]
}'
If direct access works and LiteLLM fails, the gateway layer is the next place to inspect.
Step 2: check LiteLLM parameter handling
Review your LiteLLM settings. Some deployments use:
litellm_settings:
drop_params: false
litellm_settings:
drop_params: false
This setting is often used to avoid dropping parameters that LiteLLM does not recognize. It is not a complete security policy by itself. You still need safe key injection, log redaction, and version-specific validation.
Step 3: enable debug logs
export LITELLM_LOG="DEBUG"
export LITELLM_LOG="DEBUG"
Then search the logs for:
anthropic-beta
anthropic-version
x-api-key
anthropic-beta
anthropic-version
x-api-key
Check both:
- inbound request headers received by LiteLLM
- outbound request headers sent to the backend
Fix options
- Confirm your LiteLLM version supports the beta header you need.
- Use the correct provider prefix and protocol.
- Avoid
drop_params: truewhen beta parameters need to pass through. - Configure explicit extra headers if your LiteLLM version supports that path.
- Temporarily test direct ClaudeAPI access to isolate LiteLLM from backend issues.
Problem 2: streaming is interrupted or malformed
Symptoms
stream=truestarts normally and then breaks- client reports
JSON decode error - client reports
Incomplete chunked read - LiteLLM logs show connection reset or timeout
Likely layer
| Layer | Common cause |
|---|---|
| Client | Timeout too short, SSE parsing bug, early connection close |
| LiteLLM gateway | Buffering, timeout, stream conversion, proxy settings |
| Backend | 429 rate limit, 529 overloaded, network reset |
Step 1: test streaming without LiteLLM
curl https://gw.claudeapi.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_CLAUDEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Write a short poem."}],
"stream": true
}'
curl https://gw.claudeapi.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_CLAUDEAPI_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-haiku-4-5-20251001",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Write a short poem."}],
"stream": true
}'
If direct streaming is stable but LiteLLM streaming breaks, focus on gateway timeout, buffering, and conversion.
Step 2: check LiteLLM timeouts
Exact settings vary by version, but deployments often tune values like:
litellm_settings:
request_timeout: 600
stream_timeout: 300
litellm_settings:
request_timeout: 600
stream_timeout: 300
Also check any reverse proxy in front of LiteLLM, such as Nginx, Cloudflare, ALB, API Gateway, or service mesh. Streaming often fails outside the application layer.
Step 3: verify client stream handling
Python example through LiteLLM:
import anthropic
client = anthropic.Anthropic(
api_key="YOUR_PROXY_KEY",
base_url="https://your-litellm-proxy.example.com",
)
with client.messages.stream(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a short poem."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
import anthropic
client = anthropic.Anthropic(
api_key="YOUR_PROXY_KEY",
base_url="https://your-litellm-proxy.example.com",
)
with client.messages.stream(
model="claude-haiku-4-5-20251001",
max_tokens=1024,
messages=[{"role": "user", "content": "Write a short poem."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
Check:
- the client points to LiteLLM, not direct ClaudeAPI
- client timeout is longer than LiteLLM’s stream timeout
- upstream proxies do not buffer or cut long responses
- the client handles final stream events correctly
Problem 3: model routing causes 404 or 400
Symptoms
404 model not found400 Bad Request- LiteLLM routes to the wrong backend
- model alias does not match the request
LiteLLM has two model names:
model_name: the alias your application sendslitellm_params.model: the provider/model path LiteLLM uses internally
Check registered models
curl http://localhost:4000/v1/models
curl http://localhost:4000/v1/models
The response should include the model_name values defined in model_list.
Correct Anthropic-native routing
model_list:
- model_name: claude-sonnet-5
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/CLAUDE_API_KEY
api_base: https://gw.claudeapi.com
model_list:
- model_name: claude-sonnet-5
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/CLAUDE_API_KEY
api_base: https://gw.claudeapi.com
Then test through LiteLLM:
curl http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-liteellm-proxy-key" \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "hi"}]
}'
curl http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-liteellm-proxy-key" \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "hi"}]
}'
If this returns 404, compare the request’s "model" value with model_list[].model_name.
Fix options
- Use
anthropic/when the backend is Anthropic Messages API. - Use
openai/when the backend is OpenAI-compatible Chat Completions. - Pair
anthropic/withhttps://gw.claudeapi.com. - Pair
openai/withhttps://gw.claudeapi.com/v1. - Add aliases for clients that send different model names.
Problem 4: MCP OAuth session state is lost
Symptoms
401 UnauthorizedMissing OAuth session state- first OAuth step succeeds but callback fails
- sessions interfere across users or keys
Why it happens
MCP OAuth flows require server-side state, such as state, code verifier, and callback mapping.
In a LiteLLM or gateway deployment, state can be lost if:
- multiple instances do not share session storage
- load balancer sends callback to a different instance
- headers or query parameters are rewritten
redirect_uripoints to the wrong component- OAuth callback route is not mapped to the original session
What to check
- Deployment mode:
- single instance: inspect local session behavior
- multi-instance: use Redis or another shared session store if your deployment requires it
- Callback URL:
- confirm
redirect_uripoints to the proxy callback endpoint when the proxy owns the flow
- confirm
- State consistency:
- compare
statein authorization URL, callback, and LiteLLM logs
- compare
- Parameter filtering:
- make sure state and OAuth parameters are not being dropped or renamed
Fix options
- Use shared session storage for multi-instance deployments.
- Keep OAuth callback routing sticky or state-aware.
- Avoid filtering OAuth parameters.
- Check LiteLLM version notes and MCP/OAuth documentation for current behavior.
General troubleshooting table
| Error | Most likely cause | First place to check |
|---|---|---|
401 Unauthorized |
API key or header passthrough problem | Does outbound request include expected key/header? |
404 model not found |
Model alias mismatch | model_list.model_name vs request "model" |
400 Bad Request |
Protocol mismatch | anthropic/ vs openai/, and matching api_base |
429 Rate Limit |
Backend rate limit | Retry/fallback and key usage |
529 Overloaded |
Model overload | Fallback model and backoff |
| Streaming breaks | Timeout or proxy buffering | request_timeout, stream_timeout, reverse proxy |
Missing OAuth session state |
Session state not shared | Redis/session store and callback routing |
| Beta feature missing | anthropic-beta lost |
drop_params, headers, debug logs |
Production checklist
Before putting LiteLLM in production, run through this list.

| Check | Passing standard |
|---|---|
| API key injection | Use environment variables or secret manager; no keys in Git |
| Protocol pairing | anthropic/ with https://gw.claudeapi.com; openai/ with /v1 |
| Header passthrough | anthropic-beta, anthropic-version, and auth headers are not filtered |
| Streaming | Both direct backend and LiteLLM streaming tests pass |
| Timeouts | Client timeout > LiteLLM stream timeout; LiteLLM timeout > backend P95 latency |
| Multi-instance | OAuth/MCP sessions use shared state where needed |
| Logs | DEBUG only during troubleshooting; production logs redact keys |
| Rollback | litellm_config.yaml is versioned and can be reverted quickly |
The value of this checklist is moving from “it worked once” to “it survives streaming, concurrency, failover, and OAuth callbacks.”
FAQ
Should LiteLLM connect directly to Anthropic or to ClaudeAPI?
Both patterns can work. Choose based on account, billing, network, operations, and compliance needs. If using ClaudeAPI, configure the gateway URL shown in the ClaudeAPI console. If using Anthropic directly, follow Anthropic’s official endpoint and credential docs.
Is drop_params: false unsafe?
Not by itself. It controls whether LiteLLM drops unsupported or unknown parameters. It does not replace key management, log redaction, or explicit allowlists. Use it intentionally and validate what reaches the backend.
Is every streaming issue a LiteLLM issue?
No. Test direct backend streaming first. If direct streaming fails, investigate backend, rate limit, or network. If direct works and LiteLLM fails, focus on gateway timeouts, buffering, and conversion.
What is the difference between anthropic/claude-sonnet-5 and openai/claude-sonnet-5?
anthropic/ means LiteLLM should use Anthropic-style Messages API behavior. openai/ means it should use OpenAI-compatible Chat Completions behavior. ClaudeAPI supports both access styles, but the provider prefix determines how LiteLLM constructs the request.
How do I see the actual headers LiteLLM sends?
Enable debug logs:
export LITELLM_LOG="DEBUG"
export LITELLM_LOG="DEBUG"
Then make a request and inspect the outbound request in logs. If logs are not enough, use a local HTTP debugging proxy only in a controlled non-production environment. Never expose API keys in shared logs.
Do I need to restart LiteLLM after config changes?
It depends on deployment method and parameter type. If you start LiteLLM from a config file, many changes require restart. Some UI or dynamic configuration paths may support hot updates. Check your LiteLLM version documentation.
Related guides
- Claude API Base URL configuration guide
- Claude API rate limit and concurrency guide
- Migrate from OpenAI API to Claude API
- Claude API Python tutorial
- Claude API error guide
Sources
- LiteLLM documentation
- LiteLLM Anthropic provider documentation
- LiteLLM proxy documentation
- LiteLLM GitHub repository
- Model Context Protocol authorization specification
- ClaudeAPI
ClaudeAPI is an independent third-party technical service provider. It is not affiliated with Anthropic and does not guarantee the availability of Anthropic services.



