Repository navigation
.NET: [Bug]: In-memory response streams can end before a synthesized terminal event is published #8884
Copy link
Copy link
Labels
.NETUsage: [Issues, PRs], Target: .NetUsage: [Issues, PRs], Target: .NethostingUsage: [Issues, PRs], Target: all hosting related solutionsUsage: [Issues, PRs], Target: all hosting related solutionsreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
Description
Activity
- added.NETUsage: [Issues, PRs], Target: .NetUsage: [Issues, PRs], Target: .NettriageUsage: [Issues], Target: All issues that still need to be triagedUsage: [Issues], Target: All issues that still need to be triaged
on Sep 30, 2026 - addedreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
on Sep 30, 2026 github-actions commented
on Sep 30, 2026 on Sep 30, 2026 – with GitHub ActionsContributorMore actions🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
The bug reproduces in dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/InMemoryResponsesService.cs::ExecuteResponseAsync, where synthesized terminal snapshots at lines 557-611 become visible before AddStreamingEvent publishes their events. It triggers when a streaming reader acquires ResponseState._lock between those operations and observes IsTerminal with no unread events. Minimal repro: hold the response publication lock, release a gated executor into completed, failed, or cancelled handling, then call MoveNextAsync reentrantly and observe false.
- Failing test:
dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/InMemoryResponsesServiceTerminalEventRaceTests.cs - Files examined: dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/InMemoryResponsesService.cs, dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/InMemoryResponsesServiceTests.cs, dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Models/CreateResponse.cs, dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/ResponseSessionRegressionTests.cs
- Tests run: InMemoryResponsesServiceTerminalEventRaceTests (written; 3/3 failed), InMemoryResponsesServiceTests (3/3 passed)
- Failing test:
- addedhostingUsage: [Issues, PRs], Target: all hosting related solutionsUsage: [Issues, PRs], Target: all hosting related solutionsand removedtriageUsage: [Issues], Target: All issues that still need to be triagedUsage: [Issues], Target: All issues that still need to be triaged
on Sep 30, 2026 - assigned and unassigned
on Sep 30, 2026
Metadata
Metadata
Assignees
Labels
.NETUsage: [Issues, PRs], Target: .NetUsage: [Issues, PRs], Target: .NethostingUsage: [Issues, PRs], Target: all hosting related solutionsUsage: [Issues, PRs], Target: all hosting related solutionsreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
Type
Projects
- StatusShow more project fieldsDone
Description
InMemoryResponsesServicecan finish a streaming reader without delivering the terminal event it synthesizes for completion, failure, or cancellation.The three fallback paths in
ExecuteResponseAsyncassign a terminalstate.Responsebefore callingAddStreamingEvent. A reader can acquire the state lock between those operations, see no new events together withIsTerminal == true, and returnfalsefromMoveNextAsync. The terminal event is appended afterward, too late for that reader.Expected: publishing a synthesized terminal response and its streaming event is atomic from a reader's perspective.
This concerns service-synthesized terminal events. Executor-provided terminal events already go through
AddStreamingEvent.Code Sample
The relevant interleaving is:
A controlled service-level regression holds the publication lock while the producer reaches the append, then starts a reader reentrantly. On the current implementation,
MoveNextAsyncreturnsfalsefor all three outcomes. These cases reproduced in five consecutive runs. This is a service-level reproduction, not an HTTP/SSE timing test.Error Messages / Stack Traces
No exception is required: the reader ends without the synthesized
response.completed,response.failed, orresponse.cancelledevent.Package Versions
Microsoft.Agents.AI.Hosting.OpenAI, source at2d995049b9b08949dd12bebb61236f9300c707c4. The affected .NET code is unchanged from the reproduction baseline3a837e3272268217fcc8d588700cf6e78fc413ef..NET Version
.NET SDK 10.0.401 on Linux x64; affected unit tests also run on .NET 8 and 9.
Additional Context
I'll send a focused fix that keeps each terminal snapshot local until the existing
AddStreamingEventlock publishes it with the event. Regression coverage includes the three terminal outcomes, partial output, independent readers, replay, reader-cancellation isolation, and executor-provided terminal events. No public API or persistence-policy change is needed.