Skip to content

fix: add resilient SSE streaming and HTTP keep-alive for Copilot Studio - #675

Merged
tracyboehrer (tracyboehrer) merged 1 commit into
mainfrom
users/sigaur/fix-dropped-connections
Aug 3, 2026
Merged

fix: add resilient SSE streaming and HTTP keep-alive for Copilot Studio#675
tracyboehrer (tracyboehrer) merged 1 commit into
mainfrom
users/sigaur/fix-dropped-connections

Conversation

@siddharthh98

Copy link
Copy Markdown
Contributor

Summary

Fixes the recurring Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. error on long Copilot Studio generative turns in the genesys-handoff sample.

Root Cause

The SSE stream enumeration (SendActivityAsync / StartConversationAsync) has no exception handling and no retry. On long generative turns (~10s+ silence on the wire), an idle-sensitive intermediary RSTs the outbound socket (SocketException 10054). The unguarded IOException propagates to the CloudAdapter default error handler, which leaks the raw transport error to the end user.

Changes

File Change
GenesysHandoffAgent.Streaming.cs (new) Partial class with resilient HandleNewConversation and HandleCopilotStudioMessage — retry-with-backoff (3 attempts, 500ms exponential), idempotency guard (no retry after assistant reply surfaced), friendly fallback on exhaustion
Services/McsHttpClientRegistration.cs (new) AddResilientMcsHttpClient() extension configuring the mcs HttpClient with SocketsHttpHandler keep-alive pings (15s, WithActiveRequests) and SetHandlerLifetime(Infinite)
GenesysHandoffAgent.cs Marked class partial; removed unguarded streaming methods (moved to partial file)
Program.cs Replaced AddHttpClient() with AddResilientMcsHttpClient()
README.md Added Production Hardening section (Blob Storage RBAC, MSAL distributed cache, persistent IStorage)

Testing

  • Build verified: 0 warnings, 0 errors
  • No new package dependencies (retry is implemented inline)

Wrap the Copilot Studio SSE stream enumeration (SendActivityAsync /
StartConversationAsync) in retry-with-backoff to handle transient TCP
resets from idle-sensitive intermediaries on long generative turns.

Changes:
- GenesysHandoffAgent.Streaming.cs: partial class with resilient
  HandleNewConversation and HandleCopilotStudioMessage (3 attempts,
  exponential backoff, idempotency guard, friendly fallback message).
- Services/McsHttpClientRegistration.cs: configures the named 'mcs'
  HttpClient with SocketsHttpHandler keep-alive pings and infinite
  handler lifetime to prevent mid-stream handler recycling.
- Program.cs: use AddResilientMcsHttpClient() instead of bare
  AddHttpClient().
- README.md: add Production Hardening section documenting Blob Storage
  RBAC, MSAL distributed cache, and persistent IStorage requirements.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the Samples Changes to Samples label Aug 3, 2026
@tracyboehrer
tracyboehrer (tracyboehrer) merged commit d65e444 into main Aug 3, 2026
11 checks passed
@tracyboehrer
tracyboehrer (tracyboehrer) deleted the users/sigaur/fix-dropped-connections branch August 3, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Samples Changes to Samples

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants