Skip to content

.NET: Intermittent HTTP 400 during GPT-5.6 approval resume through the documented stateless MAF Responses path #7067

Description

@CumulusService

Description

Microsoft Agent Framework / GPT-5.6 stateless approval-resume failure

Status: final submission report — sanitized HTTP 400 evidence captured on the exact 10.5.2 package set

Suggested title

Intermittent HTTP 400 during GPT-5.6 approval resume through the documented stateless Microsoft Agent Framework Responses path after the approved tool executes

Product area

  • Microsoft Agent Framework for .NET
  • Microsoft.Agents.AI.Foundry
  • Microsoft.Extensions.AI.OpenAI
  • Azure AI Foundry / Azure OpenAI Responses API
  • Human-in-the-loop ApprovalRequiredAIFunction

Summary

We are evaluating GPT-5.6 for two chat tiers in a production Microsoft Agent Framework application. The existing production route remains unchanged and healthy on Azure OpenAI Chat Completions. GPT-5.6 requires a Responses-based route for our combination of explicit reasoning and function tools.

Using the documented MAF composition over the Foundry project Responses client, a protected tool correctly pauses and emits ToolApprovalRequestContent without executing. Resuming the session with the correlated approval is intermittent. On the exact 10.5.2 candidate, iteration 1 passed both model tiers; iteration 2 failed on Terra/low during approval_resume with System.ClientModel.ClientResultException, HTTP 400, and Azure apim-request-id e03444a9-8d3c-41c2-ba9d-b3309d929790. The approved synthetic tool had executed exactly once before the provider rejected the continuation. We stopped immediately without another provider call.

No customer data or side-effecting tool is involved. The reproduction tool only increments an in-memory counter and returns a constant string.

Supported structure under test

The candidate migration uses Microsoft Agent Framework APIs only:

  1. AIProjectClient
  2. GetProjectOpenAIClient().GetProjectResponsesClient()
  3. AsIChatClientWithStoredOutputDisabled(modelId, includeReasoningEncryptedContent: true)
  4. UseFunctionInvocation(...)
  5. AsAIAgent(new ChatClientAgentOptions { ... })
  6. ApprovalRequiredAIFunction
  7. CreateSessionAsync() / RunStreamingAsync(...)
  8. ToolApprovalRequestContent.CreateResponse(...)
  9. Resume with RunStreamingAsync(new ChatMessage(ChatRole.User, [approval]), session)

We are not asking Microsoft to validate a raw ResponsesClient, hand-written protocol adapter, mixed API route, or previous_response_id workaround.

Package combinations validated

Live confirmed failing candidate

  • Target framework: net9.0
  • Microsoft.Agents.AI: 1.6.2
  • Microsoft.Agents.AI.Foundry: 1.6.2-preview.260521.1
  • Microsoft.Extensions.AI.OpenAI: 10.5.2
  • Azure.AI.Projects: 2.1.0-beta.2
  • OpenAI: 2.10.0

Newer-package offline reconnaissance

  • Microsoft.Agents.AI: 1.13.0
  • Microsoft.Agents.AI.Foundry: 1.13.0-preview.260703.1
  • Microsoft.Extensions.AI.OpenAI: 10.6.0
  • Azure.AI.Projects: 2.1.0-beta.4
  • OpenAI: 2.10.0

Both combinations restore and build with zero warnings/errors and pass the same eight offline MAF contracts. The newer combination was not treated as a fix and was not deployed.

Azure deployments exercised

  • gpt-5.6-terra, reasoning effort low
  • gpt-5.6-sol, reasoning effort medium

Resource endpoint, subscription, tenant, resource group, Container App configuration, tokens, and customer identifiers are intentionally omitted from this public-safe report. They can be supplied through an authenticated Microsoft support channel if required.

Minimal reproduction behavior

  1. Construct the stateless project Responses IChatClient with encrypted reasoning included.
  2. Wrap a no-side-effect AIFunction in ApprovalRequiredAIFunction.
  3. Create a MAF session.
  4. Prompt the model to call the protected tool exactly once.
  5. Consume the first stream.
  6. Assert that exactly one ToolApprovalRequestContent is present and the tool execution count remains zero.
  7. Create an approved response correlated to the emitted call.
  8. Resume the same MAF session with the approval response.
  9. Assert exactly one tool execution and one correlated FunctionResultContent.
  10. Repeat the exact two-tier test in a bounded loop.

Expected behavior

  • Every first stream pauses before tool execution.
  • Every approved resume executes the tool exactly once.
  • The resume stream exposes the correlated function result and completes normally.
  • Stateless requests use store=false, send neither previous_response_id nor conversation, and include reasoning.encrypted_content.

Actual behavior

  • Offline request and MAF contracts pass consistently.
  • In the latest 10.5.2 bounded live run, iteration 1 passed both Terra/low and Sol/medium.
  • Iteration 2 failed on Terra/low at 2026-07-11T12:39:18.3248089+00:00 during approval_resume with System.ClientModel.ClientResultException and HTTP 400.
  • Azure correlation header: apim-request-id: e03444a9-8d3c-41c2-ba9d-b3309d929790.
  • The response did not provide an allowlisted x-ms-error-code header, so the captured error code is null. No response body or exception message was retained.
  • The approved synthetic tool execution count was exactly one when the provider rejected the continuation.
  • An earlier 10.5.1 spike recorded in the frozen plan reported invalid_payload with protected-only reasoning. Because that earlier raw artifact was not retained, it remains historical context and a root-cause hypothesis; the new 10.5.2 evidence independently establishes HTTP 400 without claiming a body error code.
  • Source inspection found the MEAI OpenAI conversion shape constructs ReasoningResponseItem(reasoningContent.Text) and assigns EncryptedContent = ProtectedData. That shape remained textually unchanged across the inspected 10.5.1, 10.5.2, 10.6.0, and 10.7.0 source revisions.

The protected-only reasoning conversion is our leading localized hypothesis, not a claim about Azure parser internals. The end-to-end intermittent failure is the blocker regardless of mechanism.

Controls used to validate the report

  • Eight exact offline contracts passed on the default package set, Matrix A, and Matrix B.
  • Contracts cover stateless wire shape, denial, serialized cross-agent resume, dual approval correlation, replay idempotency, cancellation, skills/tool-loop caps, and streaming terminal order.
  • Default and both matrix builds completed with zero warnings/errors.
  • Fable independently reviewed the probe across multiple rounds, required corrections to package defaults, test discrimination, failure coverage, and redaction controls, and approved after every accepted finding was corrected and re-proven.
  • Claude Code Opus 4.8 independently returned PASS in read/check mode.
  • The repository diff-scoped Semgrep/gitleaks gate returned zero findings and zero vulnerable-package signals. Because the untracked probe sources were outside that diff scan, they also received dedicated line-level source and redaction reviews: Fable approved and Claude Code returned PASS before live capture and external packaging.
  • Product backend/ and frontend/ trees have zero drift against the frozen production-evidence SHA.
  • No product implementation, feature flag, QA deployment, upload, Avatar, SAP, database, or secret mutation occurred.

Questions for Microsoft

  1. Is this exact stateless MAF approval-resume structure supported for GPT-5.6 deployments when encrypted reasoning is included?
  2. Is a protected-only reasoning item with empty text/summary and non-empty encrypted content valid input on approval resume?
  3. Is there a released or planned version of Microsoft.Extensions.AI.OpenAI, Microsoft.Agents.AI.Foundry, or the Azure Responses service that changes this replay shape or fixes the rejection?
  4. Is there a different officially documented MAF-only construction for stateless GPT-5.6 Responses with human tool approval, streaming, tools, and skills?
  5. Can Microsoft provide a documented mitigation that preserves store=false, disables provider-side response/conversation chaining, preserves encrypted reasoning, and does not bypass approval semantics?

Evidence included in the support package

  • 02-diagnostic-evidence.public.json — sanitized HTTP 400 metadata, request ID, exact package versions, final probe hashes, and pass/failure sequence.
  • 03-offline-matrix-proof.json — final-source Matrix A build and 9/9 offline contract proof.
  • 04-independent-review-summary.json — public-safe Fable and Claude Code review summary.
  • repro/ — the complete four-file instrumented approval-resume reproduction.
  • SHA256SUMS.txt — integrity manifest for every other package file.

Captured diagnostic safety boundary

The submission evidence includes the exact UTC timestamp, Azure apim-request-id, exception type, HTTP status, model/tier, phase, package versions, method-only stack frames, execution count, and probe hashes. It contains no bearer token, endpoint, request or response body, exception message, encrypted reasoning, customer/tenant data, file path, or unrelated Container App configuration. The capture projection and all assertion/exception paths were reviewed by Fable and independently passed Claude Code read/check review before the live call.

Current disposition

Migration and QA deployment remain blocked. The existing production model route is unchanged. We will reconsider only after Microsoft supplies a documented MAF-compatible fix or mitigation and it passes the complete parity suite, including uploads and Avatar.

microsoft-maf-gpt56-20260711.zip

Code Sample

var responsesClient = projectClient
      .GetProjectOpenAIClient()
      .GetProjectResponsesClient();

  var statelessClient =
      responsesClient.AsIChatClientWithStoredOutputDisabled(
          modelId,
          includeReasoningEncryptedContent: true);

  var tool = new ApprovalRequiredAIFunction(
      AIFunctionFactory.Create(
          () => "APPROVAL_EXECUTED",
          "approval_probe"));

  var agent = (ChatClientAgent)statelessClient
      .AsBuilder()
      .UseFunctionInvocation()
      .Build()
      .AsAIAgent(new ChatClientAgentOptions
      {
          ChatOptions = new ChatOptions
          {
              ModelId = modelId,
              Tools = [tool],
          },
          UseProvidedChatClientAsIs = true,
      });

  var session = await agent.CreateSessionAsync();

  var first = await agent.RunAsync(
      "Call approval_probe now.",
      session);

  var request = first.Messages
      .SelectMany(m => m.Contents)
      .OfType<ToolApprovalRequestContent>()
      .Single();

  var approval = request.CreateResponse(
      approved: true,
      reason: request.ToolCall.CallId);

  // Intermittently fails with HTTP 400:
  var resumed = await agent.RunAsync(
      new ChatMessage(ChatRole.User, [approval]),
      session);

Error Messages / Stack Traces

Exception: System.ClientModel.ClientResultException
  HTTP status: 400
  Phase: approval_resume
  Model: gpt-5.6-terra
  Reasoning effort: low
  UTC: 2026-07-11T12:39:18.3248089Z
  Azure apim-request-id: e03444a9-8d3c-41c2-ba9d-b3309d929790
  Synthetic tool execution count: 1

  Sanitized stack:
  OpenAI.ClientPipelineExtensions__ProcessMessageAsync_d__0.MoveNext
  OpenAI.Responses.ResponsesClient__CreateResponseAsync_d__48.MoveNext
  OpenAI.Responses.ResponsesClient___c__DisplayClass18_0___CreateResponseStreamingAsync_b__0_d.MoveNext

  No response body or exception message was retained.

Package Versions

Microsoft.Agents.AI: 1.6.2 Microsoft.Agents.AI.Foundry: 1.6.2-preview.260521.1 Microsoft.Extensions.AI.OpenAI: 10.5.2 Azure.AI.Projects: 2.1.0-beta.2 OpenAI: 2.10.0 Target framework: .NET 9.0

.NET Version

Microsoft.Agents.AI: 1.6.2 Microsoft.Agents.AI.Foundry: 1.6.2-preview.260521.1 Microsoft.Extensions.AI.OpenAI: 10.5.2 Azure.AI.Projects: 2.1.0-beta.2 OpenAI: 2.10.0 Target framework: .NET 9.0

Additional Context

microsoft-maf-gpt56-20260711.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETUsage: [Issues, PRs], Target: .Netlikely-fixedtriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    Fields

    No fields configured for Bug.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions