github/copilot-sdk · warning

Request cancelled by runtime

Error message

Request cancelled by runtime: ${item.cancel.reason}

What it means

The handler's request-body stream is fed by the runtime over the bridge; when the runtime cancels the request it enqueues a cancel marker into the buffer. When requestBody() dequeues that marker it stops draining and throws, carrying the runtime's cancellation reason when one was provided. This surfaces cancellation to the handler's body-reading loop.

Solutions

  1. Wrap the request-processing flow in try/catch and treat cancellation as normal termination: stop work, clean up, and do not attempt further writes.
  2. Check the handler's cancellation state before/after expensive operations and bail out early instead of waiting for the next read to throw.
  3. Keep body reads timely; don't block long before reading so cancellation is noticed promptly.
  4. Log item.cancel.reason context (connection closed vs timeout) to distinguish user aborts from runtime timeouts.

Example fix

// before
for await (const chunk of handler.requestBody()) { await slowProcess(chunk); }
// after
try {
  for await (const chunk of handler.requestBody()) {
    if (handler.isCancelled) break;
    await slowProcess(chunk);
  }
} catch (e) {
  if (String(e.message).startsWith('Request cancelled by runtime')) return; // normal abort
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isRuntimeCancellation(e: unknown): boolean {
  return e instanceof Error && e.message.startsWith('Request cancelled by runtime');
}

Type guard

const isCancellationError = (e: unknown): e is Error =>
  e instanceof Error && /Request cancelled by runtime/.test(e.message);

Try / catch

try {
  for await (const chunk of handler.requestBody()) { await handle(chunk); }
} catch (e) {
  if (isCancellationError(e)) { await cleanup(); return; } // expected abort
  throw e;
}

Prevention

When it happens

Trigger: The Copilot runtime cancels the in-flight request (client disconnect, timeout, explicit cancel) while the handler is still reading request body chunks; the next requestBody() pull dequeues the cancel item and throws.

Common situations: Client closed the WebSocket connection mid-stream; request exceeded a runtime timeout; handler performs slow work (long awaits between reads) while the user aborts the request.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at nodejs/src/copilotRequestHandler.ts:557

     * Request body bytes, yielded as they arrive. A cancel frame surfaces as a
     * thrown error so the handler's upstream call is torn down.
     */
    get requestBody(): AsyncIterable<Uint8Array> {
        return {
            [Symbol.asyncIterator]: (): AsyncIterator<Uint8Array> => ({
                next: async (): Promise<IteratorResult<Uint8Array>> => {
                    if (this.#drained) {
                        return { value: undefined, done: true };
                    }
                    while (this.#buffer.length === 0) {
                        await new Promise<void>((resolve) => {
                            this.#waker = resolve;
                        });
                    }
                    const item = this.#buffer.shift()!;
                    if (item.cancel) {
                        this.#drained = true;
                        throw new Error(
                            item.cancel.reason
                                ? `Request cancelled by runtime: ${item.cancel.reason}`
                                : "Request cancelled by runtime"
                        );
                    }
                    if (item.end) {
                        this.#drained = true;
                        return { value: undefined, done: true };
                    }
                    return { value: item.chunk ?? new Uint8Array(), done: false };
                },
            }),
        };
    }

    // --- Response emit (driven by the handler). Strict state machine: ---
    // startResponse once -> 0..N writeResponse -> exactly one of
    // endResponse / errorResponse.

View on GitHub (pinned to cd8cf15dc3)