Cosmos DB emulator: gateway HTTP/2 client leaves unobserved read-loop task exceptions on cancelled reads — default the emulator client to HTTP/1.1
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues (related: #5364 emulator hangs via connection string, #9326 emulator not creating dbs/containers)
### Describe the bug
When connecting to the Cosmos DB emulator (`RunAsEmulator`) in gateway mode, the `CosmosClient` negotiates **HTTP/2** via ALPN. On a cold or slow emulator, reads that hit the request timeout are cancelled mid-flight, and the underlying HTTP/2 read-loop task is left **unobserved**. It surfaces later as a `TaskScheduler.UnobservedTaskException` / finalizer `AggregateException` (HTTP/2 stream aborted), which is noisy and, depending on `ThrowUnobservedTaskExceptions`, can destabilize the host.
The real Cosmos service is fine over HTTP/2; this is specific to the **emulator's** gateway endpoint, especially during the cold-start window.
### Expected behavior
When `RunAsEmulator` is used, the Aspire-provided Cosmos connection/client defaults (and the `AddAzureCosmosClient` wiring) should pin the emulator's `HttpClient` to **HTTP/1.1** gateway mode so cancelled reads cannot leave an orphaned HTTP/2 read loop — or document/expose this so consumers don't have to hand-configure it.
### Steps to reproduce
1. `AddAzureCosmosDB("cosmos").RunAsEmulator()`.
2. A service that reads a container shortly after startup, while the emulator is still warming.
3. Observe `UnobservedTaskException` / finalizer `AggregateException` for an aborted HTTP/2 read when a read is cancelled by the request timeout.
### Workaround (for reference)
We configure the emulator `CosmosClient`'s `HttpClient` for HTTP/1.1 only and raise the request timeout, on the emulator branch only:
```csharp
new CosmosClientOptions
{
HttpClientFactory = () =>
{
var handler = new SocketsHttpHandler();
var http = new HttpClient(handler)
{
DefaultRequestVersion = HttpVersion.Version11,
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact,
};
return http;
},
RequestTimeout = TimeSpan.FromSeconds(30), // emulator only; default 5s too tight on cold start
// ... emulator endpoint / connection mode
};
```
Pinning HTTP/1.1 kills the ALPN HTTP/2 read loop entirely, so a cancelled read can't leave an unobserved task. The timeout bump separately reduces cold-start cancellations.
### Environment
- Aspire 13.4.x
- Cosmos DB emulator (vnext-latest / preview), gateway mode, run mode (local dev)
Contributor guide
Research direction
Start at the AddAzureCosmosClient wiring and the RunAsEmulator branch, then inspect how the emulator CosmosClientOptions and HttpClient are constructed. Reproduce a cold-start read timeout and verify that emulator clients use HTTP/1.1 and no unobserved read-loop exceptions occur after cancellation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100