Responses API: reasoning items with content: null rejected by non-OpenAI providers like Openrouter

Resolved 💬 1 comment Opened Feb 14, 2026 by jonny64 Closed Feb 14, 2026

## Bug

When using the Responses API wire format (wire_api = "responses") with non-OpenAI providers (e.g. OpenRouter), multi-turn conversations fail because reasoning items with content: null are included in the request body. These providers reject the null field.

## Error

Invalid Responses API request: 3 validation error(s)

  • body.input[3].content: value is not a valid list
  • body.input[5].content: value is not a valid list

The server expects content to be omitted entirely (not set to null) when there's no reasoning text.

## Root Cause

In codex-rs/protocol/src/models.rs, the function should_serialize_reasoning_content returns false for None, which means #[serde(skip_serializing_if)] does NOT skip serialization, resulting in "content": null in the JSON output.

```rust
fn should_serialize_reasoning_content(content: &Option<Vec<ReasoningItemContent>>) -> bool {
match content {
Some(content) => !content
.iter()
.any(|c| matches!(c, ReasoningItemContent::ReasoningText { .. })),
None => false, // BUG: should be true to skip serializing null
}
}

The function name is inverted — it's used as skip_serializing_if, so returning true means "skip". None should return true (skip it) to omit the field.

Fix

  • None => false,
  • None => true,

Introduced In

Commit f1be7978cf (PR #2277 "Parse reasoning text content", Aug 13, 2025).

Reproduction

  1. Configure codex with wire_api = "responses" and a non-OpenAI provider
  2. Start a conversation and send multiple turns
  3. Second turn fails because reasoning items from the first response are sent back with content: null

Affected Versions

0.22.0 through at least 0.101.0-alpha.1

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗