{"record":{"id":"34a983fd70e687eb","repo":"github/copilot-sdk","slug":"copilot-request-response-start-called-twice-34a983","errorCode":null,"errorMessage":"Copilot request response start() called twice.","messagePattern":"Copilot request response start\\(\\) called twice\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/copilot/copilot_request_handler.py","lineNumber":441,"sourceCode":"\n    @property\n    def request_body(self) -> _BodyQueue:\n        return self._queue\n\n    def _require_rpc(self) -> ServerLlmInferenceApi:\n        rpc = self._get_server_rpc()\n        if rpc is None:\n            raise RuntimeError(\"Copilot request response used after RPC connection closed.\")\n        return rpc\n\n    async def start_response(\n        self,\n        status: int,\n        status_text: str | None = None,\n        headers: LlmInferenceHeaders | None = None,\n    ) -> None:\n        if self.started:\n            raise RuntimeError(\"Copilot request response start() called twice.\")\n        if self.finished:\n            raise RuntimeError(\"Copilot request response already finished.\")\n        self.started = True\n        await self._require_rpc().http_response_start(\n            LlmInferenceHTTPResponseStartRequest(\n                headers=headers or {},\n                request_id=self.request_id,\n                status=status,\n                status_text=status_text,\n            )\n        )\n\n    async def write_response(self, data: str | bytes) -> None:\n        if self.cancelled:\n            raise RuntimeError(\"Copilot request was cancelled by the runtime.\")\n        if not self.started:\n            raise RuntimeError(\"Copilot request response write() called before start().\")\n        if self.finished:","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/python/copilot/copilot_request_handler.py#L423-L459","documentation":"start_response() is the state-machine entry point for a Copilot request's HTTP response and must be called exactly once before streaming body data. The library sets a `started` flag after the first call and raises this RuntimeError on any subsequent call, since a single HTTP response can only begin once per request_id. This guards against double-issuing the LlmInferenceHTTPResponseStart RPC to the runtime.","triggerScenarios":"Calling response.start_response() a second time on the same response object — e.g. calling it again after _finalize or after _stream_response_to_exchange has already invoked it, or wrapping start_response in retry logic that retries an already-successful call.","commonSituations":"Custom response pipelines that call start_response both explicitly and via a framework adapter; error-handling paths that try to start an error response after the success path already started it; accidental double-invocation when both _finalize and a streaming helper run.","solutions":["Check `response.started` before calling start_response(), or track whether your code already started the response.","Ensure only one code path (either _finalize or _stream_response_to_exchange) is responsible for starting the response.","If you need to send headers again for a different response, create a fresh response/request instead of reusing the finished one."],"exampleFix":"// before\nawait response.start_response(200, \"OK\", headers)\nawait response.start_response(200, \"OK\", headers)  # duplicate\n\n// after\nif not response.started:\n    await response.start_response(200, \"OK\", headers)","handlingStrategy":"validation","validationCode":"if response.started:\n    raise RuntimeError(\"response already started; skipping duplicate start_response\")","typeGuard":"def can_start(response) -> bool:\n    return not getattr(response, \"started\", False) and not getattr(response, \"finished\", False)","tryCatchPattern":"try:\n    await response.start_response(status, status_text, headers)\nexcept RuntimeError as e:\n    if \"called twice\" not in str(e):\n        raise","preventionTips":["Designate a single owner for calling start_response (one code path, not both _finalize and the streamer).","Track response state locally and assert single-start in debug builds.","Never reuse a response object across requests or retries."],"tags":["state-machine","http-response","python","copilot"],"backgroundTag":"invalid-state-transition","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"}