{"record":{"id":"ae47b31fd36307ca","repo":"github/copilot-sdk","slug":"copilot-request-response-already-finished-ae47b3","errorCode":null,"errorMessage":"Copilot request response already finished.","messagePattern":"Copilot request response already finished\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/copilot/copilot_request_handler.py","lineNumber":443,"sourceCode":"    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:\n            raise RuntimeError(\"Copilot request response write() called after end()/error().\")\n        if isinstance(data, bytes):","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/python/copilot/copilot_request_handler.py#L425-L461","documentation":"After the response has been finished (end_response/error called or the handler marked it finished), start_response() refuses to begin a new response on the same object. The `finished` flag is a terminal state in the response lifecycle: started -> writing -> finished. This prevents resurrecting a completed HTTP response on a request_id the runtime has already closed.","triggerScenarios":"Calling start_response() after end_response()/error() was called; calling start_response() on a response object that _finalize already completed; attempting to restart a response to send a retry or error status after the body was fully streamed.","commonSituations":"Middleware that tries to convert an in-flight response into an error response after the fact; handlers that run cleanup/finalization before the caller finishes writing; reusing a cached response object across retries.","solutions":["Check `response.finished` before attempting start_response(); do not start a response after the body stream has ended.","Send errors before finishing: call error()/end only after all writes, and never start a new response afterwards.","Create a new request/response exchange if you genuinely need to emit another HTTP response."],"exampleFix":"// before\nawait response.write_response(\"chunk\")\nawait response.end_response()\nawait response.start_response(500, \"Error\", {})  # finished\n\n// after\nif not response.finished:\n    await response.write_response(\"chunk\")\n    await response.end_response()\nelse:\n    raise RuntimeError(\"response already completed; cannot restart\")","handlingStrategy":"validation","validationCode":"if response.finished:\n    return  # response already completed; nothing to start","typeGuard":"def is_startable(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 \"already finished\" in str(e):\n        logger.warning(\"attempted to restart finished response; ignoring\")\n    else:\n        raise","preventionTips":["Send error/status changes before finishing the response, never after end/error.","Order the lifecycle strictly: start -> writes -> end/error, with cleanup tasks cancelled before finishing.","Check `finished` before any lifecycle call on a response you did not create."],"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"}