github/copilot-sdk · error
Copilot request response start() called twice.
Error message
Copilot request response start() called twice.
What it means
The response emitter is a strict state machine: startResponse() may run exactly once per request, followed by writes and exactly one end/error. A second startResponse() call means the handler tried to begin the HTTP response twice, so the RPC httpResponseStart would be sent twice; the library prevents that by throwing.
Solutions
- Use ONE response style per handler: either the high-level helpers (finalize/streamResponse) or the low-level startResponse/writeResponse/endResponse sequence, never both.
- Track locally whether the response has started before attempting to start it in shared/retry code paths.
- If a fallback response is needed after a failure, use errorResponse() rather than starting a new response.
- Review handler code paths (e.g. branches that both finalize and stream) and remove the duplicate start.
Example fix
// before
await handler.startResponse({ status: 200 });
await handler.streamResponse(data); // throws: starts again
// after
await handler.streamResponse(data); // single entry point handles start/write/end Defensive patterns
Strategy: validation
Validate before calling
let responseStarted = false;
function beginResponse(h, init) {
if (responseStarted) return;
responseStarted = true;
return h.startResponse(init);
} Try / catch
try {
await handler.startResponse({ status: 200 });
} catch (e) {
if (e instanceof Error && e.message.includes('start() called twice')) return; // already started
throw e;
} Prevention
- Pick one response style per handler: high-level helpers OR low-level start/write/end — never mix.
- Return immediately after finishing a response so no further emit code runs.
- Centralize response emission in a single helper that guards a started flag.
- Avoid re-emit logic in catch/retry paths; use errorResponse() for failures instead.
When it happens
Trigger: Calling startResponse() explicitly and then also using streamResponse() (or finalize()), both of which invoke startResponse; or invoking startResponse() twice on the same handler instance.
Common situations: Mixing the high-level response helpers (finalize/streamResponse) with low-level start/write/end calls in the same handler; retry/fallback logic that re-sends a response after an earlier path already started it.
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
- Copilot request response already finished.
- Copilot request response write() called before start().
- Copilot request response write() called after end()/error().
- LLM inference response startResponse() called twice
- LLM inference response already finished
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/d89b939abf9c196b.
Report an issue: GitHub.
Appendix: source
Thrown at nodejs/src/copilotRequestHandler.ts:579
);
}
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.
async startResponse(init: ResponseInit): Promise<void> {
if (this.#started) {
throw new Error("Copilot request response start() called twice.");
}
if (this.#finished) {
throw new Error("Copilot request response already finished.");
}
this.#started = true;
await this.#rpc().llmInference.httpResponseStart({
requestId: this.requestId,
status: init.status,
statusText: init.statusText,
headers: init.headers ?? {},
});
}
async writeResponse(data: string | Uint8Array): Promise<void> {
if (this.#cancelled) {
throw new Error("Copilot request was cancelled by the runtime.");
}
if (!this.#started) {View on GitHub (pinned to cd8cf15dc3)