dotnet/runtime · error · Error
OperationCanceledException
Error message
OperationCanceledException
What it means
Thrown by http_wasm_get_streamed_response_bytes in http.ts when the response body reader reports done (currentStreamReaderChunk.done) and controller.isAborted is true. This converts an abort-after-stream-end into an OperationCanceledException so managed code sees the cancellation rather than a clean zero-byte read.
Solutions
- Catch OperationCanceledException (or its HttpRequestException wrapper) on the managed streaming call and treat it as cancellation, not a fault.
- Avoid disposing HttpClient or cancelling the token while a streamed read loop is in progress unless you intend to cancel.
- If you need partial results, copy bytes read before the cancellation into your own buffer.
Example fix
// before: unhandled cancellation on streamed read
try { await foreach (var b in response.Content.ReadAsStreamAsync()) { ... } }
catch { throw; }
// after: handle cancellation explicitly
try { await foreach (var b in stream.WithCancellation(token)) { ... } }
catch (OperationCanceledException) { /* expected on abort */ } Defensive patterns
Strategy: try-catch
Validate before calling
// pass the token and check before each read if (token.IsCancellationRequested) return;
Try / catch
try {
await foreach (var chunk in response.Content.ReadAsStreamAsync(token)) { ... }
} catch (OperationCanceledException) {
// the streamed response was aborted; this is expected on cancellation
} Prevention
- Always pass the CancellationToken to streaming reads (WithCancellation).
- Catch OperationCanceledException separately from other HTTP errors.
- Do not dispose HttpClient while a streamed read loop is still draining.
When it happens
Trigger: Reading a streamed HTTP response with HttpClient.SendAsync(HttpCompletionOption.ResponseHeadersRead) and the CancellationToken is cancelled, or HttpClient is disposed, while the response body is still being drained; the server completes the stream exactly when the client aborts.
Common situations: User navigates away during a large download; a CancellationTokenSource cancels a long streaming GET; component dispose cancels an in-flight HttpClient call.
Related errors
- OperationCanceledException
- BrowserHttpWriteStream.Rejected
- BrowserHttpWriteStream.Rejected
- Failed to download , Status Code
- Azure failed to download
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/cfab00f64b10808b.
Report an issue: GitHub.
Appendix: source
Thrown at src/mono/browser/runtime/http.ts:260
const view = new Span(bufferPtr, bufferLength, MemoryViewType.Byte);
return wrap_as_cancelable_promise(async () => {
await controller.responsePromise;
mono_assert(controller.response, "expected response");
if (!controller.response.body) {
// in FF when the verb is HEAD, the body is null
return 0;
}
if (!controller.streamReader) {
controller.streamReader = controller.response.body.getReader();
mute_unhandledrejection(controller.streamReader.closed);
}
if (!controller.currentStreamReaderChunk || controller.currentBufferOffset === undefined) {
controller.currentStreamReaderChunk = await controller.streamReader.read();
controller.currentBufferOffset = 0;
}
if (controller.currentStreamReaderChunk.done) {
if (controller.isAborted) {
throw new Error("OperationCanceledException");
}
return 0;
}
const remaining_source = controller.currentStreamReaderChunk.value.byteLength - controller.currentBufferOffset;
mono_assert(remaining_source > 0, "expected remaining_source to be greater than 0");
const bytes_copied = Math.min(remaining_source, view.byteLength);
const source_view = controller.currentStreamReaderChunk.value.subarray(controller.currentBufferOffset, controller.currentBufferOffset + bytes_copied);
view.set(source_view, 0);
controller.currentBufferOffset += bytes_copied;
if (remaining_source == bytes_copied) {
controller.currentStreamReaderChunk = undefined;
}
return bytes_copied;
});
}View on GitHub (pinned to 60108ba66e)