Azure-compatible enterprise proxy endpoints fail with "stream closed before response.completed" due to unsupported include and client_metadata fields

Resolved 💬 2 comments Opened Apr 15, 2026 by esteban-uo Closed Apr 15, 2026

What version of Codex CLI is running?

0.120.0

What subscription do you have?

Enterprise

Which model were you using?

gpt-5.2-codex

What platform is your computer?

Darwin 25.4.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

iTerm2

What issue are you seeing?

Environment

  • Provider type: Azure-compatible enterprise OpenAI proxy (wire_api = "responses")
  • Model: gpt-5.2-codex (any model with supports_reasoning_summaries: true)
  • Config: model_reasoning_effort set (e.g. xhigh)

## Bug

When using Codex with an Azure-compatible enterprise OpenAI proxy, every request fails immediately with:

``
■ stream disconnected before completion: stream closed before response.completed
``

The stream returns HTTP 200 but closes with an empty body, never emitting response.completed.

## Root Cause

build_responses_request() in codex-rs/core/src/client.rs sends two fields that Azure-compatible proxies do not support:

1. "reasoning.encrypted_content" in include

When reasoning.is_some(), the request always includes:

``json
"include": ["reasoning.encrypted_content"]
``

Azure/enterprise proxies reject this with {"detail": "Internal server error"}.

2. client_metadata

The request always includes:

``json
"client_metadata": {"x-codex-installation-id": "<id>"}
``

Azure/enterprise proxies silently return HTTP 200 with an empty body when this field is present, triggering the "stream closed before response.completed"
error.

Both fields are OpenAI.com-internal and are already correctly skipped in other parts of the codebase using the existing
provider.is_azure_responses_endpoint() guard — but build_responses_request() was missing these guards.

## Proposed Fix

In codex-rs/core/src/client.rs, build_responses_request():

```rust
// Before
let include = if reasoning.is_some() {
vec!["reasoning.encrypted_content".to_string()]
} else {
Vec::new()
};

// After
let include = if reasoning.is_some() && !provider.is_azure_responses_endpoint() {
vec!["reasoning.encrypted_content".to_string()]
} else {
Vec::new()
};
```

```rust
// Before
client_metadata: Some(HashMap::from([(
X_CODEX_INSTALLATION_ID_HEADER.to_string(),
self.client.state.installation_id.clone(),
)])),

// After
client_metadata: if !provider.is_azure_responses_endpoint() {
Some(HashMap::from([(
X_CODEX_INSTALLATION_ID_HEADER.to_string(),
self.client.state.installation_id.clone(),
)]))
} else {
None
},
``
Both changes reuse the existing
is_azure_responses_endpoint()` helper, consistent with how the rest of the codebase handles Azure/enterprise endpoints.

What steps can reproduce the bug?

Use CLI with given parameters.

What is the expected behavior?

_No response_

Additional information

_No response_

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗