Description
Summary
Two chained defects make the Responses API unusable against third-party OpenAI-compatible endpoints that emit "status": null on reasoning items:
- 2.13.0:
DeserializeReasoningResponseItem lacks a null guard for the optional status property, so "status": null throws ArgumentOutOfRangeException and aborts the entire SSE stream.
- 2.14.0: the null guard was added, but the release also removed the public (experimental) struct
OpenAI.Responses.GlobalMcpToolCallApprovalPolicy, which Microsoft.Extensions.AI.OpenAI 10.10.0 references from OpenAIResponsesChatClient.ToResponseTool. Any tool-carrying Responses call through the M.E.AI adapter now dies with TypeLoadException at JIT time.
Environment
- OpenAI SDK: 2.13.0 (also tested 2.14.0)
- Microsoft.Agents.AI.OpenAI 1.21.0 / Microsoft.Extensions.AI.OpenAI 10.10.0
- .NET 10
Defect 1 — 2.13.0: no null guard on status (repro)
Third-party Responses-compatible backends may serialize the optional status field of a reasoning item as JSON null instead of omitting it (same class of report as openai/openai-dotnet#1342). Since status is optional per spec, null should deserialize the same as "absent".
This issue has already been addressed openai/openai-dotnet#1346 repaired.
Minimal repro — no network or API key required, uses the exact entry point the SSE pipeline uses (AsyncSseUpdateCollection → ModelReaderWriter):
using System.ClientModel.Primitives;
using OpenAI.Responses;
var json = """
{"type":"response.output_item.added","output_index":0,
"item":{"type":"reasoning","id":"rs_1","status":null,"summary":[]}}
""";
#pragma warning disable OPENAI001
var update = ModelReaderWriter.Read<StreamingResponseUpdate>(BinaryData.FromString(json));
#pragma warning restore OPENAI001
Actual (2.13.0):
System.ArgumentOutOfRangeException: Unknown ReasoningStatus value. (Parameter 'value')
at OpenAI.Responses.ReasoningStatusExtensions.ToReasoningStatus(String value)
at OpenAI.Responses.ReasoningResponseItem.DeserializeReasoningResponseItem(...)
Expected: status: null treated as absent (ReasoningStatus? status = null).
Actual (2.14.0): passes — decompilation confirms the guard was added:
// 2.13.0 (generated) — no null check; GetString() on JSON null returns C# null
else if (item.NameEquals("status"))
{
status = item.Value.GetString().ToReasoningStatus(); // throws
}
// 2.14.0 (generated)
else if (item.NameEquals("status"))
{
if (item.Value.ValueKind != JsonValueKind.Null)
{
status = item.Value.GetString().ToReasoningStatus();
}
}
Defect 2 — 2.14.0: breaking removal of GlobalMcpToolCallApprovalPolicy
I tried importing OpenAI 2.14.0 package directly, but it caused this problem:
System.TypeLoadException: Could not load type 'OpenAI.Responses.GlobalMcpToolCallApprovalPolicy'
from assembly 'OpenAI, Version=2.14.0.0, Culture=neutral, PublicKeyToken=b4187f3e65366280'.
at Microsoft.Extensions.AI.OpenAIResponsesChatClient.ToResponseTool(AITool tool, ChatOptions options, ToolSearchLookup toolSearchLookup)
at Microsoft.Extensions.AI.OpenAIResponsesChatClient.AsCreateResponseOptions(...)
at Microsoft.Extensions.AI.OpenAIResponsesChatClient.GetStreamingResponseAsync(...)
In 2.14.0, GlobalMcpToolCallApprovalPolicy has been renamed to DefaultMcpToolCallApprovalPolicy. The following is a screenshot of openai-dotnet CHANGELOG.
Reproduction Steps
using System.ClientModel.Primitives;
using OpenAI.Responses;
var json = """
{"type":"response.output_item.added","output_index":0,
"item":{"type":"reasoning","id":"rs_1","status":null,"summary":[]}}
""";
#pragma warning disable OPENAI001
var update = ModelReaderWriter.Read<StreamingResponseUpdate>(BinaryData.FromString(json));
#pragma warning restore OPENAI001
Expected behavior
Correct deserialization to obtain StreamResponseUpdate: status: null treated as absent (ReasoningStatus? status = null).
Actual behavior
2.13.0:
System.ArgumentOutOfRangeException: Unknown ReasoningStatus value. (Parameter 'value')
at OpenAI.Responses.ReasoningStatusExtensions.ToReasoningStatus(String value)
at OpenAI.Responses.ReasoningResponseItem.DeserializeReasoningResponseItem(...)
Unhandled exception. System.ArgumentOutOfRangeException: Unknown ReasoningStatus value. (Parameter 'value')
at OpenAI.Responses.ReasoningStatusExtensions.ToReasoningStatus(String value)
at OpenAI.Responses.ReasoningResponseItem.DeserializeReasoningResponseItem...
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response
Description
Summary
Two chained defects make the Responses API unusable against third-party OpenAI-compatible endpoints that emit
"status": nullon reasoning items:DeserializeReasoningResponseItemlacks a null guard for the optionalstatusproperty, so"status": nullthrowsArgumentOutOfRangeExceptionand aborts the entire SSE stream.OpenAI.Responses.GlobalMcpToolCallApprovalPolicy, whichMicrosoft.Extensions.AI.OpenAI10.10.0 references fromOpenAIResponsesChatClient.ToResponseTool. Any tool-carrying Responses call through the M.E.AI adapter now dies withTypeLoadExceptionat JIT time.Environment
Defect 1 — 2.13.0: no null guard on
status(repro)Third-party Responses-compatible backends may serialize the optional
statusfield of a reasoning item as JSONnullinstead of omitting it (same class of report as openai/openai-dotnet#1342). Sincestatusis optional per spec,nullshould deserialize the same as "absent".This issue has already been addressed openai/openai-dotnet#1346 repaired.
Minimal repro — no network or API key required, uses the exact entry point the SSE pipeline uses (
AsyncSseUpdateCollection→ModelReaderWriter):Actual (2.13.0):
Expected:
status: nulltreated as absent (ReasoningStatus? status = null).Actual (2.14.0): passes — decompilation confirms the guard was added:
Defect 2 — 2.14.0: breaking removal of
GlobalMcpToolCallApprovalPolicyI tried importing OpenAI 2.14.0 package directly, but it caused this problem:
In 2.14.0,
GlobalMcpToolCallApprovalPolicyhas been renamed toDefaultMcpToolCallApprovalPolicy. The following is a screenshot of openai-dotnet CHANGELOG.Reproduction Steps
Expected behavior
Correct deserialization to obtain StreamResponseUpdate:
status: nulltreated as absent (ReasoningStatus? status = null).Actual behavior
2.13.0:
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response