Title:
[Bug] Claude→OpenAI translator passes content as array instead of string, crashing NVIDIA NIM / vLLM backends with "sequence item 0: expected str instance, list found"
Body:
## Bug Description
When using `openai-compatibility` to route Claude Code (`/v1/messages`) requests to an OpenAI-compatible backend (specifically **NVIDIA NIM** running DeepSeek V4-Pro), the Claude→OpenAI message translator passes `content` as a **Claude-format array** `[{"type":"text","text":"..."}]` instead of flattening it to an **OpenAI-format string** `"..."`.
The upstream backend (Python-based vLLM/NVIDIA NIM) receives the array-typed content and crashes with:
TypeError: sequence item 0: expected str instance, list found
This is related but distinct from #1136 (which covers empty system message arrays). This bug affects **all message roles** (user, assistant, system) when their `content` field contains Claude's array format.
## Environment
- **CLIProxyAPI Version:** 6.9.38 (commit `2c626efc`, built 2026-04-25)
- **OS:** Windows 11 (24H2), x64
- **Upstream:** NVIDIA NIM (`https://integrate.api.nvidia.com/v1`)
- **Model:** `deepseek-ai/deepseek-v4-pro`
- **Client:** Claude Code CLI v2.1.119
## Configuration
```yaml
openai-compatibility:
- name: nvidia
base-url: https://integrate.api.nvidia.com/v1
api-key-entries:
- api-key: nvap***
models:
- name: deepseek-ai/deepseek-v4-pro
payload:
override:
- models:
- name: deepseek-ai/deepseek-v4-pro
protocol: openai
params:
chat_template_kwargs.thinking: false
max_tokens: 16384
Steps to Reproduce
- Configure CLIProxyAPI with
openai-compatibility pointing to NVIDIA NIM
- Point Claude Code at CLIProxyAPI via
ANTHROPIC_BASE_URL=http://localhost:8317
- Set model to
deepseek-ai/deepseek-v4-pro
- Send any message through Claude Code
Actual Behavior
What Claude Code sends (/v1/messages):
{
"model": "deepseek-ai/deepseek-v4-pro",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "hello"}
]
}
],
"max_tokens": 8192,
"stream": true
}
What gets forwarded to NVIDIA NIM (/v1/chat/completions) — BUG HERE:
The translator converts the request but keeps content as an array (or produces malformed nested structures). The NVIDIA Python backend receives non-string content and crashes:
HTTP 500 Internal Server Error: sequence item 0: expected str instance, list found
Error log from CLIProxyAPI v6.9.38:
openai_compat_executor.go:258] request error, error status: 500,
error message: "Internal server error: sequence item 0: expected str instance, list found"
After this error, the credential enters cooldown and all subsequent requests return:
503 auth_unavailable: no auth available (providers=nvidia, model=deepseek-ai/deepseek-v4-pro)
Expected Behavior
The Claude→OpenAI translator should flatten all content arrays to strings before forwarding:
{
"model": "deepseek-ai/deepseek-v4-pro",
"messages": [
{
"role": "user",
"content": "hello"
}
],
"max_tokens": 8192,
"stream": true
}
The flattening logic should handle:
- Simple text blocks:
[{"type":"text","text":"..."}] → "..."
- Mixed content (text + tool_use + tool_result): concatenate text parts, stringify tool parts
- System messages with array content
- Multi-turn conversations with assistant
content arrays containing both text and tool_use blocks
Workaround
I wrote a Node.js translation proxy that sits between Claude Code and CLIProxyAPI, correctly flattening content arrays. The chain is:
Claude Code → :8318 (translator-proxy, flattens content) → :8317 (CLIProxyAPI) → NVIDIA NIM
With this workaround in place, requests succeed with HTTP 200 (50-80s response time on DeepSeek V4-Pro). This proves the issue is specifically in CLIProxyAPI's internal/translator/claude/openai/chat-completions/ path.
Key workaround code — the flatten function:
function flattenContent(content) {
if (typeof content === 'string') return content;
if (Array.isArray(content)) {
return content
.filter(block => block.type === 'text')
.map(block => block.text || '')
.join('');
}
return String(content || '');
}
Root Cause Analysis
The relevant code is likely in:
internal/translator/claude/openai/chat-completions/claude_openai_request.go
The converter needs a flattenContent() function that:
- If
content is a string → pass through unchanged
- If
content is an array → extract all type:"text" blocks' .text fields, join them into a single string
- For assistant messages with
tool_use blocks → convert to OpenAI tool_calls format with function.name / function.arguments
- For user messages with
tool_result blocks → convert to readable string representation
Additional Context
- Direct OpenAI-format calls work fine: When calling
/v1/chat/completions directly (bypassing the Claude→OpenAI translation), NVIDIA NIM responds correctly with HTTP 200
- The issue only occurs through the
/v1/messages → OpenAI translation path
- Related issues: #1136 (system empty array), #1043 (assistant array dropped), #1670 (tool_result array handling)
- Alternative project that handles this correctly: anyllm-proxy (Rust-based proxy purpose-built for Anthropic↔OpenAI translation)
Impact
This blocks all Claude Code users from using NVIDIA NIM (and likely other strict OpenAI-compatible backends such as vLLM, Ollama, TGI) through CLIProxyAPI's openai-compatibility feature. NVIDIA NIM is one of the most popular free-tier LLM providers, so this affects a significant number of users.
Title:
Body:
TypeError: sequence item 0: expected str instance, list found
Steps to Reproduce
openai-compatibilitypointing to NVIDIA NIMANTHROPIC_BASE_URL=http://localhost:8317deepseek-ai/deepseek-v4-proActual Behavior
What Claude Code sends (
/v1/messages):{ "model": "deepseek-ai/deepseek-v4-pro", "messages": [ { "role": "user", "content": [ {"type": "text", "text": "hello"} ] } ], "max_tokens": 8192, "stream": true }What gets forwarded to NVIDIA NIM (
/v1/chat/completions) — BUG HERE:The translator converts the request but keeps
contentas an array (or produces malformed nested structures). The NVIDIA Python backend receives non-string content and crashes:Error log from CLIProxyAPI v6.9.38:
After this error, the credential enters cooldown and all subsequent requests return:
Expected Behavior
The Claude→OpenAI translator should flatten all content arrays to strings before forwarding:
{ "model": "deepseek-ai/deepseek-v4-pro", "messages": [ { "role": "user", "content": "hello" } ], "max_tokens": 8192, "stream": true }The flattening logic should handle:
[{"type":"text","text":"..."}]→"..."contentarrays containing bothtextandtool_useblocksWorkaround
I wrote a Node.js translation proxy that sits between Claude Code and CLIProxyAPI, correctly flattening content arrays. The chain is:
With this workaround in place, requests succeed with HTTP 200 (50-80s response time on DeepSeek V4-Pro). This proves the issue is specifically in CLIProxyAPI's
internal/translator/claude/openai/chat-completions/path.Key workaround code — the flatten function:
Root Cause Analysis
The relevant code is likely in:
internal/translator/claude/openai/chat-completions/claude_openai_request.goThe converter needs a
flattenContent()function that:contentis a string → pass through unchangedcontentis an array → extract alltype:"text"blocks'.textfields, join them into a single stringtool_useblocks → convert to OpenAItool_callsformat withfunction.name/function.argumentstool_resultblocks → convert to readable string representationAdditional Context
/v1/chat/completionsdirectly (bypassing the Claude→OpenAI translation), NVIDIA NIM responds correctly with HTTP 200/v1/messages→ OpenAI translation pathImpact
This blocks all Claude Code users from using NVIDIA NIM (and likely other strict OpenAI-compatible backends such as vLLM, Ollama, TGI) through CLIProxyAPI's
openai-compatibilityfeature. NVIDIA NIM is one of the most popular free-tier LLM providers, so this affects a significant number of users.