{"record":{"id":"1ededa7c919263a7","repo":"github/copilot-sdk","slug":"request-cancelled-by-runtime-item-cancel-reason","errorCode":null,"errorMessage":"Request cancelled by runtime: ${item.cancel.reason}","messagePattern":"Request cancelled by runtime: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"nodejs/src/copilotRequestHandler.ts","lineNumber":557,"sourceCode":"     * Request body bytes, yielded as they arrive. A cancel frame surfaces as a\n     * thrown error so the handler's upstream call is torn down.\n     */\n    get requestBody(): AsyncIterable<Uint8Array> {\n        return {\n            [Symbol.asyncIterator]: (): AsyncIterator<Uint8Array> => ({\n                next: async (): Promise<IteratorResult<Uint8Array>> => {\n                    if (this.#drained) {\n                        return { value: undefined, done: true };\n                    }\n                    while (this.#buffer.length === 0) {\n                        await new Promise<void>((resolve) => {\n                            this.#waker = resolve;\n                        });\n                    }\n                    const item = this.#buffer.shift()!;\n                    if (item.cancel) {\n                        this.#drained = true;\n                        throw new Error(\n                            item.cancel.reason\n                                ? `Request cancelled by runtime: ${item.cancel.reason}`\n                                : \"Request cancelled by runtime\"\n                        );\n                    }\n                    if (item.end) {\n                        this.#drained = true;\n                        return { value: undefined, done: true };\n                    }\n                    return { value: item.chunk ?? new Uint8Array(), done: false };\n                },\n            }),\n        };\n    }\n\n    // --- Response emit (driven by the handler). Strict state machine: ---\n    // startResponse once -> 0..N writeResponse -> exactly one of\n    // endResponse / errorResponse.","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/copilotRequestHandler.ts#L539-L575","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the request-processing flow in try/catch and treat cancellation as normal termination: stop work, clean up, and do not attempt further writes.","Check the handler's cancellation state before/after expensive operations and bail out early instead of waiting for the next read to throw.","Keep body reads timely; don't block long before reading so cancellation is noticed promptly.","Log item.cancel.reason context (connection closed vs timeout) to distinguish user aborts from runtime timeouts."],"exampleFix":"// before\nfor await (const chunk of handler.requestBody()) { await slowProcess(chunk); }\n// after\ntry {\n  for await (const chunk of handler.requestBody()) {\n    if (handler.isCancelled) break;\n    await slowProcess(chunk);\n  }\n} catch (e) {\n  if (String(e.message).startsWith('Request cancelled by runtime')) return; // normal abort\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"function isRuntimeCancellation(e: unknown): boolean {\n  return e instanceof Error && e.message.startsWith('Request cancelled by runtime');\n}","typeGuard":"const isCancellationError = (e: unknown): e is Error =>\n  e instanceof Error && /Request cancelled by runtime/.test(e.message);","tryCatchPattern":"try {\n  for await (const chunk of handler.requestBody()) { await handle(chunk); }\n} catch (e) {\n  if (isCancellationError(e)) { await cleanup(); return; } // expected abort\n  throw e;\n}","preventionTips":["Poll the handler's cancelled state between expensive operations.","Keep request processing short and responsive to reads.","Log cancel reasons to distinguish disconnects from timeouts.","Clean up resources (streams, temp files) on cancellation."],"tags":["cancellation","streaming","runtime"],"backgroundTag":"request-timeout","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}