github/copilot-sdk · error · RuntimeError

Copilot request response used after RPC connection closed.

Error message

Copilot request response used after RPC connection closed.

What it means

_require_rpc fetches the ServerLlmInferenceApi RPC channel backing the response object; if the connection has been closed the channel is None. Any response use after that point (start_response, write_response, end_response, error_response) raises RuntimeError because responses can no longer be delivered over the dead RPC connection.

Solutions

  1. Check connection state (or wrap calls in try/except RuntimeError) before using the response object
  2. Complete all response writes while the RPC connection is alive; buffer and resend across a new connection if needed
  3. Treat connection-closed during response as terminal: discard the response and let the client retry the request
  4. Keep a reconnect mechanism and re-issue the whole request rather than reusing the stale response object

Example fix

// before
await response.start_response(200)  # raises after disconnect
// after
try:
    await response.start_response(200)
    await response.write_response(data)
    await response.end_response()
except RuntimeError as e:
    if "RPC connection closed" in str(e):
        logger.warning("dropped response: connection closed")
Defensive patterns

Strategy: try-catch

Validate before calling

def can_respond(response) -> bool:
    try:
        return response._get_server_rpc() is not None
    except Exception:
        return False

Try / catch

try:
    await response.start_response(200)
    await response.write_response(data)
    await response.end_response()
except RuntimeError as e:
    if "RPC connection closed" in str(e):
        logger.warning("response aborted: RPC closed")
    else:
        raise

Prevention

When it happens

Trigger: Calling start_response/write_response/end_response/error_response after the underlying RPC connection to the Copilot server has closed — e.g. the handler finishes after disconnect, or the response is stored and used later.

Common situations: Responding to a request after the client/server connection dropped, holding response objects across reconnects, slow response writing that outlives the connection, or shutdown racing with response generation.

Understand the failure class

Related errors


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

Appendix: source

Thrown at python/copilot/copilot_request_handler.py:431

        """Fill in the request context once the matching start frame arrives."""
        self.session_id = params.session_id
        self.agent_id = params.agent_id
        self.parent_agent_id = params.parent_agent_id
        self.interaction_type = params.interaction_type
        self.method = params.method
        self.url = params.url
        self.headers = params.headers
        transport = params.transport
        self.transport = transport.value if transport is not None else "http"

    @property
    def request_body(self) -> _BodyQueue:
        return self._queue

    def _require_rpc(self) -> ServerLlmInferenceApi:
        rpc = self._get_server_rpc()
        if rpc is None:
            raise RuntimeError("Copilot request response used after RPC connection closed.")
        return rpc

    async def start_response(
        self,
        status: int,
        status_text: str | None = None,
        headers: LlmInferenceHeaders | None = None,
    ) -> None:
        if self.started:
            raise RuntimeError("Copilot request response start() called twice.")
        if self.finished:
            raise RuntimeError("Copilot request response already finished.")
        self.started = True
        await self._require_rpc().http_response_start(
            LlmInferenceHTTPResponseStartRequest(
                headers=headers or {},
                request_id=self.request_id,
                status=status,

View on GitHub (pinned to cd8cf15dc3)