Repository navigation
.NET: runtime logs forwarding session.idle, but SDK callback never delivers it #2008
Description
Activity
github-actions commented
on Jul 16, 2026 on Jul 16, 2026 – with GitHub ActionsContributorMore actionsInvestigation findings
Label applied:
bugI investigated the reported behavior by examining the following files:
dotnet/src/Session.cs— event channel andSendAndWaitAsyncimplementationdotnet/src/Client.cs—OnSessionEvent/OnSessionLifecycleRPC handlers and method registrationsdotnet/src/JsonRpc.cs— JSON-RPC dispatchdotnet/src/Generated/SessionEvents.cs—SessionIdleEventdeserialization
What I found
The SDK architecture is designed to deliver
session.idlereliably to live subscribers:session.idleis properly registered as a[JsonDerivedType]and will be deserialized toSessionIdleEvent.OnSessionEventinClient.cscallssession.DispatchEvent(evt), whichTryWrites it into an unboundedChannel<SessionEvent>and thenProcessEventsAsyncdelivers it serially to all registered handlers — no filtering or intentional dropping occurs in the SDK.SendAndWaitAsyncregisters itsSessionIdleEventhandler before callingSendAsync, so there is no subscription race window on the SDK side.- The CLI confirms it forwards the ephemeral
session.idleevent — yet the SDK subscriber misses it in practice on Windows ARM64.
Root cause assessment
This is a genuine bug. The SDK's completion contract (
SendAndWaitAsync) has no fallback when the livesession.idlenotification is dropped in transit. Because the event is ephemeral it cannot be recovered viaGetEventsAsync(), leaving the caller waiting indefinitely (until the 60 s timeout fires).Previous issue #794 identified this drop and proposed a fallback that was never shipped. Issue #558 tracks the resulting timeout. The combination confirms this is a known but unresolved transport reliability gap, not intentional design.
What a fix should address
- Resilient idle detection: the runtime/SDK should provide a
WaitForIdleAsync-style RPC call that is answered by the runtime rather than relying solely on an ephemeral push notification. - Dispatch flush guarantee: callers awaiting completion need assurance that all preceding events have been delivered to FIFO handlers before the idle signal resolves.
- Regression test: a test that deliberately drops the live
session.idlenotification and verifies the completion wait still resolves (e.g., via a polled RPC fallback) would prevent recurrence.
I have applied the
buglabel accordingly.Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
awmgmcpg
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:network: allowed: - defaults - "awmgmcpg"
See Network Configuration for more information.
Generated by Bug Handler for #2008 · 52.8 AIC · ⌖ 5.09 AIC · ⊞ 4.5K · ◷
Thanks for reporting this, and especially for including the CLI-side event sequence. We want to ensure Visual Studio has a reliable completion contract.
One requested requirement is:
Remain correct if the live
session.idlenotification is dropped.The transport is intended to provide reliable, ordered event delivery. We generally cannot support individual events being unpredictably dropped: losing assistant, tool, permission, or error events could break much more than idle detection. We are not currently aware of a confirmed case where one event disappears while the connection remains healthy. If that can be reproduced, we would prioritize investigating and fixing the delivery defect itself.
There is a separate consideration around resume:
session.idleis ephemeral and is not persisted or replayed. A completed session resumed later will not receive another copy of its previous idle event. An active resumed session should receive a future idle event when its work finishes.Could you share a small code sample or pseudocode showing:
- whether the session is created or resumed;
- if resumed, whether Visual Studio expects the prior idle state or sends new work first;
- whether it uses
SendAndWaitAsyncor an event subscription; and - the short event sequence observed by that subscription?
For example:
var session = await client.CreateSessionAsync(...); // or ResumeSessionAsync(...) session.On<SessionEvent>(evt => Log(evt.Type)); await session.SendAsync(...); // or SendAndWaitAsync(...)
If the same live subscription receives
assistant.turn_endfor newly sent work but not the followingsession.idle, that would be particularly important. A minimal console reproduction would be ideal, but stripped-down pseudocode from the Visual Studio flow is enough to begin; no wire-level trace is needed at this stage.
Summary
Visual Studio can remain indefinitely in the running state after the Copilot CLI has completed a response.
The CLI verbose log shows that it forwarded the terminal, ephemeral
session.idleevent. The .NET SDK subscription receives the preceding events, including the finalassistant.turn_end, but never receivesSessionIdleEvent.Because
session.idleis ephemeral, it cannot be recovered later throughGetEventsAsync(). The client therefore has no authoritative completion signal and continues waiting.Environment
1.0.11.0.61The current SDK
1.0.7still does not expose a durable idle wait or event-dispatch flush API.Observed behavior
The CLI log ends with:
The .NET subscriber observes
assistant.turn_end, but notsession.idle. The operation remains active even though the runtime has finished.The absence of
session.idlefromevents.jsonlis expected because the event is ephemeral; it does not explain why the live subscriber missed it.Why
assistant.turn_endis insufficientIn multi-turn tool workflows, an intermediate
assistant.turn_endcan be followed immediately by anotherassistant.turn_start. Completion also needs to account for sub-agents, attached shells, queued messages, and follow-up notification turns.Requested fix
Please provide an authoritative completion contract owned by the runtime/SDK, for example:
It should:
session.idlenotification is dropped.An equivalent
session.waitForIdleruntime RPC combined with an SDK dispatch barrier would also work.Regression test
Add a .NET test where a multi-turn response completes, the live
session.idlenotification is deliberately dropped, and the completion wait still resolves only after all preceding assistant and tool events have been delivered.Related
processQueuedItems()lacks error handling —session.idlemay never be emitted #794 - identifies missingsession.idleand silent notification drops, but its proposed fallback did not shipsession.idlesendAndWaitreturns prematurely when orchestrator dispatches sub-agents viataskand ends its turn #1275 -assistant.turn_endalone is not a completion signal