Regression after #34645: proxied Azure Responses provider sends item IDs with store=false and fails with "Item with id ... not found"

Resolved 💬 0 comments Opened Jul 28, 2026 by MineIdea Closed Jul 28, 2026

What version of Codex CLI is running?

codex-cli 0.146.0-alpha.3.1

What subscription do you have?

ChatGPT Pro; the affected Codex Desktop task uses a custom enterprise model provider backed by Azure OpenAI.

Which model were you using?

gpt-5.5

What platform is your computer?

Darwin 25.3.0 arm64 arm

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

iTerm2

Codex doctor report

not available

What issue are you seeing?

Summary

Codex Desktop fails during multi-turn tool use when configured with a custom Responses provider that forwards to Azure OpenAI through a local proxy.

After #34645 ("Always assign response item IDs"), Codex no longer has a supported request shape for this provider topology. The local proxy hides the final Azure hostname from Codex, so Codex does not recognize the provider as Azure and sends:

{
  "store": false,
  "input": [
    {
      "type": "reasoning",
      "id": "rs_REDACTED",
      "encrypted_content": "REDACTED"
    }
  ]
}

The upstream returns:

Item with id 'rs_REDACTED' not found. Items are not persisted when `store` is set to false. Try again with `store` set to true, or remove this item from your input.

Behavior before #34645

With Codex 0.145.0 and the default item_ids = false behavior:

| Provider/request mode | store | Outbound input item IDs | Result |
|---|---:|---:|---|
| Custom provider through local proxy | false | Omitted | Succeeds |
| Provider detected as Azure | true | Included | Succeeds |

When store was false and item IDs were disabled, prepare_response_items_for_request() removed all outbound input[].id values.

Behavior after #34645

Starting with 0.146.0-alpha.1:

| Provider/request mode | store | Outbound input item IDs | Result |
|---|---:|---:|---|
| Custom provider through local proxy | false | Included | Fails |
| Provider detected as Azure | true | Included | Succeeds |

The item_ids feature is now marked as removed/default-enabled, and item_ids = false is accepted only as a compatibility no-op. At the same time, store is still determined only by:

store: provider.is_azure_responses_endpoint()

Therefore, a custom provider behind a local proxy cannot:

  1. Restore the previous store=false plus no outbound item IDs behavior; or
  2. Explicitly request store=true without changing or spoofing the provider name.

Confirmed workaround

Changing only the custom provider display name from:

name = "proxy"

to the exact value:

name = "azure"

causes is_azure_responses_endpoint() to return true. Codex then sends store=true while preserving the same rs_... item IDs, and the same multi-turn tool-call task succeeds.

This confirms that the model, tool call, item ID, encrypted reasoning content, proxy, and upstream credentials are valid. The failure is caused by the unavailable request-shaping combination.

What steps can reproduce the bug?

  1. Configure Codex Desktop with a custom Responses provider whose local URL forwards to Azure OpenAI:

```toml
model = "gpt-5.5"
model_provider = "proxy"

[model_providers.proxy]
name = "Proxy"
base_url = "http://127.0.0.1:PORT/v1"
wire_api = "responses"

[features]
item_ids = false
```

The proxy is a transparent forwarder. Replace PORT with the local port. Authentication configuration is omitted here.

  1. Fully quit and restart Codex Desktop so the embedded runtime reloads the provider configuration.
  1. Start a new Codex task.
  1. Use a prompt that reliably creates both a reasoning item and a function call:

``text
First reason carefully and compute 17*23+19. Then use the terminal to run
printf codex-item-id-test. Finally reply with the calculation result and terminal output. Do not use other tools.
``

  1. Observe the follow-up /v1/responses request after the terminal result is returned. It contains:

``json
{
"store": false,
"input": [
{
"type": "reasoning",
"id": "rs_REDACTED",
"encrypted_content": "REDACTED"
},
{
"type": "function_call",
"id": "fc_REDACTED",
"call_id": "call_REDACTED"
},
{
"type": "function_call_output",
"id": "fco_REDACTED",
"call_id": "call_REDACTED"
}
]
}
``

  1. The upstream returns HTTP 400:

``text
Item with id 'rs_REDACTED' not found. Items are not persisted when
store is set to false.
``

  1. Fully quit Codex Desktop and change only the provider name to:

``toml
name = "azure"
``

  1. Restart Codex Desktop, start a new task, and repeat the same prompt.
  1. The request now contains store=true, preserves the same item types and IDs, and succeeds.

What is the expected behavior?

A custom Responses provider should be able to declare its upstream storage and request-shaping behavior without relying on provider display-name or base-URL detection.

Minimal compatibility option

The smallest compatibility change may be to keep #34645's internal stable item IDs while restoring features.item_ids = false only as an outbound serialization control.

In other words:

  • Codex would continue assigning and retaining stable IDs internally for history, streaming, fork, resume, and compaction.
  • When store=false and item_ids=false, Codex would omit only the top-level input[].id fields from the serialized request.
  • When store=true, IDs would still be serialized regardless of item_ids=false, preserving the existing Azure stored-response behavior.

The resulting behavior would be:

| store | item_ids | Outbound input[].id |
|---:|---:|---|
| false | false | Omitted |
| false | true | Included |
| true | false | Included |
| true | true | Included |

This could reuse the existing configuration:

[features]
item_ids = false

but give it the narrower post-#34645 meaning of controlling outbound serialization only.

If reusing a removed feature name is undesirable, the same behavior could be exposed through a new, explicit option:

[features]
serialize_item_ids = false

or preferably as a provider-scoped option:

[model_providers.proxy]
serialize_item_ids = false

Conceptually, the outbound request preparation would retain internal IDs and clear IDs only on the request copy:

if !store && !serialize_item_ids {
    for item in input {
        item.set_id(None);
    }
}
Alternative: explicit store override

Another option would be an explicit typed provider setting:

[model_providers.proxy]
responses_store = true

with behavior equivalent to:

store: provider
    .responses_store
    .unwrap_or_else(|| provider.is_azure_responses_endpoint())

This would preserve all current defaults while allowing an Azure endpoint hidden behind a local or corporate proxy to use the existing Azure behavior.

The key requirement is that custom providers have a supported way to select a valid request shape. Changing the provider display name to exactly azure should not be the only workaround.

Additional information

Affected surfaces

This issue was originally discovered in Codex Desktop while using a custom corporate Responses provider that forwards to Azure OpenAI.

The same request-shaping behavior was later reproduced independently with the public Codex CLI 0.146.0-alpha.3.1. The CLI reproduction isolates the problem to the shared codex-rs request construction behavior rather than the Desktop UI or Electron layer.

Relevant changes
  • #34645 made response item IDs always assigned and retired features.item_ids as a configurable feature.
  • #3528 introduced the existing Azure store=true workaround.
  • #5458 is a related request for provider-specific request-body configuration.
  • #31875 is an adjacent Azure/custom-provider compatibility report.
Current Azure documentation

Current Azure documentation supports both stored and stateless Responses usage. In stateless mode, it documents store=false together with reasoning.encrypted_content:

<https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/responses#encrypted-reasoning-items>

The failing request in this report already contains non-empty encrypted_content. This issue does not assume that every Azure endpoint must use store=true; it requests an explicit custom-provider capability so Codex does not have to infer storage semantics from the visible proxy URL or display name.

Regression range
  • rust-v0.145.0 does not contain #34645; default stateless requests omit outbound item IDs.
  • rust-v0.146.0-alpha.1 and later contain #34645; prefixed item IDs are always preserved in outbound requests.

View original on GitHub ↗