dotnet/runtime · error · Error

OperationCanceledException

Error message

OperationCanceledException

What it means

Thrown by http_wasm_get_streamed_response_bytes when the response stream reader reports done (chunk.done) AND controller.isAborted is true. It represents a deliberately-cancelled HTTP read being surfaced to .NET as an OperationCanceledException-equivalent.

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 290d5ab72c)

Solutions

  1. Treat OperationCanceledException as expected when a CancellationToken is in play — catch OperationCanceledException in your .NET HttpClient code.
  2. Ensure you are not aborting requests unintentionally (e.g., disposing the HttpResponseMessage or HttpClient prematurely).
  3. If aborts are unexpected, audit cancellation token sources for accidental cancellation.

Example fix

// before: unhandled cancellation on streaming response
// var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct);
// await resp.Content.ReadAsStreamAsync(ct); // throws OperationCanceledException

// after: handle cancellation explicitly
try {
  var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct);
  await ProcessStreamAsync(await resp.Content.ReadAsStreamAsync(ct), ct);
} catch (OperationCanceledException) when (ct.IsCancellationRequested) {
  // expected
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct);
} catch (OperationCanceledException) when (ct.IsCancellationRequested) {
  // expected cancellation
}

Prevention

When it happens

Trigger: Produced when consuming a streamed response body after the request was aborted (http_wasm_abort set controller.isAborted) and the reader has reached the end of the stream.

Common situations: A CancellationToken cancels an HttpClient.SendAsync with streaming response; the abort fires concurrently with the stream finishing; client navigates away during a long download.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/cfab00f64b10808b. Report an issue: GitHub.