openclaw - ✅(Solved) Fix [Bug]: NVIDIA Provider Sends Anthropic-Style Message Format to OpenAI-Compatible API [1 pull requests, 3 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#50107Fetched 2026-04-08 00:59:04
View on GitHub
Comments
3
Participants
2
Timeline
8
Reactions
0
Author
Timeline (top)
commented ×3cross-referenced ×2labeled ×2referenced ×1

When spawning a subagent with sessions_spawn using an NVIDIA provider configured as "api": "openai-completions", OpenClaw sends Anthropic-style message content (arrays with type objects) instead of OpenAI-style simple strings. This causes the NVIDIA API to reject requests with HTTP 400 errors.

bug was created using openclaw *

Error Message

  1. Observe HTTP 400 error in logs Error Logs 2026-03-19T00:20:40.070Z warn agent/embedded {"subsystem":"agent/embedded"} "error":"HTTP 400: "[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 2026-03-19T00:20:40.081Z warn agent/embedded {"subsystem":"agent/embedded"} error=400 "[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), Key error from NVIDIA API:
  • Error occurs on first API call before agent runs
  • Compaction attempts also fail with same error

Root Cause

Suspected Root Cause

Fix Action

Fix / Workaround

Workaround - None currently available. The message format is constructed internally by OpenClaw's subagent spawning system and cannot be overridden via configuration or agent instructions.

PR fix notes

PR #50152: fix: normalize text-only content arrays to strings for OpenAI-compatible providers

Description (problem / solution / changelog)

Summary

  • Problem: When using an NVIDIA provider (or other third-party OpenAI-compatible providers like vLLM, Ollama, LiteLLM) configured as api: "openai-completions", pi-ai's convertMessages emits user messages with Anthropic-style content block arrays ([{type:"text", text:"..."}]) instead of plain strings. This causes NVIDIA NIM and similar providers to reject requests with HTTP 400 errors.
  • Why it matters: Users cannot use NVIDIA or other strict OpenAI-compatible providers for subagent runs or any session where the internal message representation passes through pi-ai's conversion layer.
  • What changed: Added a createOpenAICompatContentNormalizationWrapper stream wrapper that normalizes text-only content arrays to plain strings via the onPayload hook for openai-completions payloads. Applied universally in applyExtraParamsToAgent.
  • What did NOT change (scope boundary): Messages with mixed content (text + images) are left as arrays. Non-openai-completions API types (Responses, Anthropic, Google) are not affected. The pi-ai library itself is not modified.

AI-assisted: This PR was authored with AI assistance. Fully tested with colocated vitest tests. The fix was verified by reading the pi-ai convertMessages source to confirm the root cause.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway / orchestration

Linked Issue/PR

  • Fixes #50107

User-visible / Behavior Changes

NVIDIA and other third-party OpenAI-compatible providers now work correctly when configured as openai-completions. Messages are sent in the standard {"role": "user", "content": "string"} format instead of the Anthropic-style {"role": "user", "content": [{"type": "text", "text": "string"}]} format.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Any
  • Runtime/container: Node 22+
  • Model/provider: NVIDIA NIM (nvidia/llama-3.1-nemotron-70b-instruct), vLLM, or any strict OpenAI-compatible provider
  • Integration/channel: Any

Steps

  1. Configure an NVIDIA provider with api: "openai-completions"
  2. Spawn a subagent or send a message through the provider
  3. Observe the outbound payload

Expected

User messages in the payload use plain string content: {"role": "user", "content": "Say hello"}

Actual

User messages use Anthropic-style content arrays: {"role": "user", "content": [{"type": "text", "text": "Say hello"}]}, causing HTTP 400 from NVIDIA API.

Evidence

  • Failing test/log before + passing after

9 new tests cover the fix:

  • Flattens text-only user content arrays to plain strings for NVIDIA
  • Flattens multi-block text-only content into a single string
  • Preserves mixed content arrays with images untouched
  • Does not modify already-plain-string content
  • Normalizes system message content arrays to strings
  • Applies to any openai-completions provider, not just NVIDIA
  • Skips normalization for non-openai-completions API types
  • Handles empty content arrays gracefully
  • Flattens assistant text-only content arrays to strings

Human Verification (required)

  • Verified scenarios: All 9 test cases pass. Reviewed pi-ai convertMessages source to confirm root cause at node_modules/@mariozechner/pi-ai/dist/providers/openai-completions.js:420-452.
  • Edge cases checked: Empty arrays (left alone), mixed text+image content (left alone), multi-block text (concatenated), already-string content (pass-through), non-openai-completions API (skipped).
  • What I did not verify: Live NVIDIA API call (no API key available in test environment).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert the single commit. The wrapper is applied in extra-params.ts and can be commented out.
  • Files/config to restore: src/agents/pi-embedded-runner/extra-params.ts, src/agents/pi-embedded-runner/openai-stream-wrappers.ts
  • Known bad symptoms reviewers should watch for: If any OpenAI-compatible provider specifically requires array-format content for text-only messages (unlikely), it would break. Native OpenAI is unaffected since the wrapper only fires for openai-completions API type.

Risks and Mitigations

  • Risk: A provider might rely on the array format for cache_control annotations on text blocks (e.g., OpenRouter Anthropic caching adds cache_control to text blocks).
    • Mitigation: The normalization only flattens arrays where ALL blocks are plain {type:"text"} objects. Blocks with extra properties like cache_control would cause allText to still be true, but the OpenRouter caching wrapper runs on a separate code path (system/developer roles only) and is applied before this wrapper in the chain. The concatenation preserves all text content.

Changed files

  • src/agents/pi-embedded-runner/extra-params.nvidia-content-normalization.test.ts (added, +244/-0)
  • src/agents/pi-embedded-runner/extra-params.ts (modified, +7/-0)
  • src/agents/pi-embedded-runner/openai-stream-wrappers.ts (modified, +90/-0)

Code Example

{
  "models": {
    "providers": {
      "nvidia": {
        "baseUrl": "https://integrate.api.nvidia.com/v1",
        "apiKey": "nvapi-XXXXXXXXXX",
        "api": "openai-completions",
        "models": [
          {
            "id": "qwen/qwen2.5-7b-instruct",
            "name": "Qwen 2.5 7B Instruct",
            "reasoning": false,
            "input": ["text"],
            "cost": {
              "input": 0,
              "output": 0,
              "cacheRead": 0,
              "cacheWrite": 0
            },
            "contextWindow": 128000,
            "maxTokens": 8192
          }
        ]
      }
    }
  }
}


## Steps to Reproduce

1. Configure NVIDIA provider with `"api": "openai-completions"`
2. Spawn a subagent using the NVIDIA model:

---

3. Observe HTTP 400 error in logs


### Expected behavior


OpenClaw should send OpenAI-compatible message format:

---

### Actual behavior

## Actual Behavior

OpenClaw sends Anthropic-style message format:

---

Error Logs


2026-03-19T00:20:40.070Z warn agent/embedded {"subsystem":"agent/embedded"} 
{"event":"embedded_run_agent_end","tags":["error_handling","lifecycle","agent_end","assistant_error"],
"runId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","isError":true,
"error":"HTTP 400: \"[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': '[Wed 2026-03-18 19:20 CDT] 
[Subagent Context] You are running as a subagent (depth 1/1). Results auto-announce to your requester...",
"failoverReason":"auth","model":"qwen/qwen2.5-7b-instruct","provider":"nvidia",
"rawErrorPreview":"400 \"[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': ..."}

2026-03-19T00:20:40.081Z warn agent/embedded {"subsystem":"agent/embedded"} 
[context-overflow-diag] sessionKey=agent:test-agent:subagent:XXXXXXXX provider=nvidia/qwen/qwen2.5-7b-instruct 
source=assistantError messages=2 compactionAttempts=0 observedTokens=unknown 
error=400 "[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': ...


**Key error from NVIDIA API:**

'msg': 'Input should be a valid string'
'loc': ('body', 'messages', 1, 'content')



## Verification: Direct API Call Works

To confirm the NVIDIA API itself works correctly, a direct curl request succeeds:


curl -X POST "https://integrate.api.nvidia.com/v1/chat/completions" \
  -H "Authorization: Bearer nvapi-XXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen/qwen2.5-7b-instruct",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Say hello"}
    ],
    "temperature": 0.6,
    "max_tokens": 100
  }'


**Response (success):**

{
  "id": "chat-XXXXXXXX",
  "object": "chat.completion",
  "created": 1773879437,
  "model": "qwen/qwen2.5-7b-instruct",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! I'm here and ready to help."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 32,
    "total_tokens": 44,
    "completion_tokens": 12
  }
}

This confirms:
-NVIDIA API works with OpenAI-style simple string content
-Model responds correctly
-Authentication is valid
-OpenClaw is sending wrong message format
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Summary

When spawning a subagent with sessions_spawn using an NVIDIA provider configured as "api": "openai-completions", OpenClaw sends Anthropic-style message content (arrays with type objects) instead of OpenAI-style simple strings. This causes the NVIDIA API to reject requests with HTTP 400 errors.

bug was created using openclaw *

Steps to reproduce

Configuration

{
  "models": {
    "providers": {
      "nvidia": {
        "baseUrl": "https://integrate.api.nvidia.com/v1",
        "apiKey": "nvapi-XXXXXXXXXX",
        "api": "openai-completions",
        "models": [
          {
            "id": "qwen/qwen2.5-7b-instruct",
            "name": "Qwen 2.5 7B Instruct",
            "reasoning": false,
            "input": ["text"],
            "cost": {
              "input": 0,
              "output": 0,
              "cacheRead": 0,
              "cacheWrite": 0
            },
            "contextWindow": 128000,
            "maxTokens": 8192
          }
        ]
      }
    }
  }
}


## Steps to Reproduce

1. Configure NVIDIA provider with `"api": "openai-completions"`
2. Spawn a subagent using the NVIDIA model:
   ```javascript
   sessions_spawn({
     agentId: "test-agent",
     mode: "run",
     model: "nvidia/qwen/qwen2.5-7b-instruct",
     task: "Say hello",
     runTimeoutSeconds: 60
   })
  1. Observe HTTP 400 error in logs

Expected behavior

OpenClaw should send OpenAI-compatible message format:

{
  "model": "qwen/qwen2.5-7b-instruct",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": "Say hello"
    }
  ]
}

Actual behavior

Actual Behavior

OpenClaw sends Anthropic-style message format:

{
  "model": "qwen/qwen2.5-7b-instruct",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "Say hello"
        }
      ]
    }
  ]
}

### OpenClaw version

2026.3.13 (61d171a)

### Operating system

 macOS (Darwin 25.3.0, arm64)

### Install method

npm global

### Model

nvidia/qwen/qwen2.5-7b-instruct

### Provider / routing chain

openclaw -> nvidia

### Config file / key location

~/.openclaw/openclaw.json ; models.providers.nvidia.baseUrl ; https://integrate.api.nvidia.com/v1

### Additional provider/model setup details

(Note: Default is anthropic/claude-sonnet-4-5, but bug occurs when spawning with NVIDIA model)

### Logs, screenshots, and evidence

```shell
Error Logs


2026-03-19T00:20:40.070Z warn agent/embedded {"subsystem":"agent/embedded"} 
{"event":"embedded_run_agent_end","tags":["error_handling","lifecycle","agent_end","assistant_error"],
"runId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","isError":true,
"error":"HTTP 400: \"[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': '[Wed 2026-03-18 19:20 CDT] 
[Subagent Context] You are running as a subagent (depth 1/1). Results auto-announce to your requester...",
"failoverReason":"auth","model":"qwen/qwen2.5-7b-instruct","provider":"nvidia",
"rawErrorPreview":"400 \"[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': ..."}

2026-03-19T00:20:40.081Z warn agent/embedded {"subsystem":"agent/embedded"} 
[context-overflow-diag] sessionKey=agent:test-agent:subagent:XXXXXXXX provider=nvidia/qwen/qwen2.5-7b-instruct 
source=assistantError messages=2 compactionAttempts=0 observedTokens=unknown 
error=400 "[{'type': 'string_type', 'loc': ('body', 'messages', 1, 'content'), 
'msg': 'Input should be a valid string', 'input': [{'type': 'text', 'text': ...


**Key error from NVIDIA API:**

'msg': 'Input should be a valid string'
'loc': ('body', 'messages', 1, 'content')



## Verification: Direct API Call Works

To confirm the NVIDIA API itself works correctly, a direct curl request succeeds:


curl -X POST "https://integrate.api.nvidia.com/v1/chat/completions" \
  -H "Authorization: Bearer nvapi-XXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen/qwen2.5-7b-instruct",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Say hello"}
    ],
    "temperature": 0.6,
    "max_tokens": 100
  }'


**Response (success):**

{
  "id": "chat-XXXXXXXX",
  "object": "chat.completion",
  "created": 1773879437,
  "model": "qwen/qwen2.5-7b-instruct",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! I'm here and ready to help."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 32,
    "total_tokens": 44,
    "completion_tokens": 12
  }
}

This confirms:
- ✅ NVIDIA API works with OpenAI-style simple string content
- ✅ Model responds correctly
- ✅ Authentication is valid
- ❌ OpenClaw is sending wrong message format

Impact and severity

This prevents using any OpenAI-compatible provider that strictly validates message format according to OpenAI spec. NVIDIA NIM is one example, but this could affect other providers as well.

Workaround - None currently available. The message format is constructed internally by OpenClaw's subagent spawning system and cannot be overridden via configuration or agent instructions.

Additional information

This appears to be a second NVIDIA provider formatting bug, discovered the same day as #49369 (NVIDIA model ID prefix stripping). Both issues suggest broader request formatting problems with the NVIDIA provider:

  • #49369: Model ID field formatted incorrectly → HTTP 404
  • This issue: Message content field formatted incorrectly → HTTP 400

Both block using NVIDIA provider with sessions_spawn in OpenClaw 2026.3.13.

Additional Context

  • The "api": "openai-completions" config value is correctly set
  • Error occurs on first API call before agent runs
  • Happens with all task types (simple and complex)
  • Compaction attempts also fail with same error
  • Both mode: "run" and mode: "session" affected

Suspected Root Cause

OpenClaw's subagent system appears to always use Anthropic's message content format (content as array of objects with type field) regardless of the provider's api setting. The code likely needs to conditionally format messages based on the target provider's API type.

extent analysis

Fix Plan

To fix the issue, we need to modify the OpenClaw code to conditionally format messages based on the target provider's API type. Here are the steps:

  • Identify the code responsible for formatting messages in the subagent spawning system.
  • Add a conditional statement to check the provider's api setting.
  • If the api setting is "openai-completions", format the messages as simple strings instead of arrays with type objects.

Example code snippet:

if (provider.api === 'openai-completions') {
  // Format messages as simple strings
  message.content = message.content.text;
} else {
  // Format messages as arrays with type objects (default behavior)
  message.content = [{ type: 'text', text: message.content.text }];
}
  • Update the code to use the formatted messages when making API calls to the provider.

Verification

To verify that the fix worked, you can:

  • Spawn a subagent using the NVIDIA model and check the API request payload to ensure that the messages are formatted as simple strings.
  • Verify that the NVIDIA API returns a successful response (200 OK) and that the subagent runs correctly.
  • Test the fix with different task types and modes (e.g., mode: "run" and mode: "session") to ensure that it works consistently.

Extra Tips

  • Make sure to update the OpenClaw documentation to reflect the changes made to the code.
  • Consider adding additional logging or debugging statements to help diagnose any future issues related to message formatting.
  • If you encounter any issues during the verification process, check the API request payload and response to ensure that the messages are formatted correctly and that the API is returning the expected response.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

OpenClaw should send OpenAI-compatible message format:

{
  "model": "qwen/qwen2.5-7b-instruct",
  "messages": [
    {
      "role": "system",
      "content": "You are a helpful assistant."
    },
    {
      "role": "user",
      "content": "Say hello"
    }
  ]
}

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING