github/copilot-sdk · error · InvalidOperationException

LLM inference request was cancelled by the runtime.

Error message

LLM inference request was cancelled by the runtime.

What it means

LlmInferenceExchange.WriteChunkAsync refuses to write body chunks once the runtime has cancelled the request (_cancelled set). This InvalidOperationException prevents writing data for an aborted exchange and preserves protocol ordering (chunks only after StartAsync).

Solutions

  1. Check the cancelled state (and observe OperationCanceledException) and stop writing immediately
  2. Abort/complete the write pipeline upon cancellation instead of continuing to enqueue chunks
  3. Ensure StartAsync is awaited before any WriteAsync call (separate pre-start error)

Example fix

// before
foreach (var chunk in chunks) await exchange.WriteAsync(chunk);
// after
foreach (var chunk in chunks)
{
    if (exchange.IsCancelled) break; // stop writing after cancellation
    await exchange.WriteAsync(chunk);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (exchange.IsCancelled) return; // do not write after cancellation

Type guard

bool CanWrite(LlmInferenceExchange ex) => !ex.IsCancelled && ex.Started;

Try / catch

try { await exchange.WriteAsync(chunk); }
catch (InvalidOperationException ex) when (ex.Message.Contains("cancelled by the runtime")) { /* stop streaming; dispose exchange */ }

Prevention

When it happens

Trigger: Writing response chunks after cancellation was observed, e.g. continuing a chunk-write loop after an OperationCanceledException was caught, or queuing buffered writes that flush after cancellation; also thrown if WriteAsync is called before StartAsync (different message).

Common situations: A producer task keeps streaming into the response after the runtime aborted the request; app buffers chunks and flushes too late; cancellation was swallowed earlier so writes continue silently.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/1a66ad3475667b99. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/CopilotRequestHandler.cs:815

        {
            return;
        }

        _finished = true;
        await ServerRpc()
            .LlmInference.HttpResponseChunkAsync(
                RequestId,
                string.Empty,
                end: true,
                error: new LlmInferenceHttpResponseChunkError { Message = message, Code = code })
            .ConfigureAwait(false);
    }

    private async Task WriteChunkAsync(string data, bool binary)
    {
        if (_cancelled)
        {
            throw new InvalidOperationException("LLM inference request was cancelled by the runtime.");
        }

        if (!_started)
        {
            throw new InvalidOperationException("LLM inference response WriteAsync() called before StartAsync().");
        }

        if (_finished)
        {
            throw new InvalidOperationException("LLM inference response WriteAsync() called after EndAsync()/ErrorAsync().");
        }

        await ServerRpc()
            .LlmInference.HttpResponseChunkAsync(RequestId, data, binary: binary, end: false)
            .ConfigureAwait(false);
    }

    private ServerRpc ServerRpc() =>

View on GitHub (pinned to cd8cf15dc3)