github/copilot-sdk · error · RuntimeError

The in-process runtime connection is closed.

Error message

The in-process runtime connection is closed.

What it means

_write_frame refuses to write when the host is disposed or the connection id is zero, meaning the in-process runtime connection is no longer open. Any send attempt after dispose or a failed connection open hits this error.

Solutions

  1. Ensure start_blocking completed successfully and the host is not disposed before sending frames.
  2. Guard send paths with a lifecycle check (disposed/connection open) before writing.
  3. Re-create the host and restart the connection if it was already disposed.
  4. Synchronize shutdown so no writer threads still hold references during dispose.

Example fix

# before
host.dispose()
host.send_request("ping")  # raises
# after
if not host.is_disposed:
    host.send_request("ping")
Defensive patterns

Strategy: validation

Validate before calling

if host.is_disposed or not host.is_connected:
    raise RuntimeError("restart host before sending")

Type guard

def can_send(host) -> bool:
    return not host.is_disposed and host.is_connected

Try / catch

try:
    host.send_request("ping")
except RuntimeError as e:
    if "connection is closed" in str(e):
        host = recreate_and_start_host()

Prevention

When it happens

Trigger: Sending a request/notification on FfiRuntimeHost after dispose() was called, or before/after the connection was successfully opened (connection id 0), including queued writes racing with disposal.

Common situations: Calling client.request() after host.dispose(); a failed start_blocking left the connection closed but callers still send; background threads writing during shutdown.

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


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

Appendix: source

Thrown at python/copilot/_ffi_runtime_host.py:473

        everything is caught and logged.
        """
        with self._callback_lock:
            if self._disposed:
                return
            self._active_callbacks += 1
        try:
            if bytes_ptr and bytes_len > 0:
                data = ctypes.string_at(bytes_ptr, bytes_len)
                self._receive_buffer.feed(data)
        except Exception:  # noqa: BLE001
            logger.error("In-process FFI inbound callback failed", exc_info=True)
        finally:
            with self._callback_lock:
                self._active_callbacks -= 1

    def _write_frame(self, frame: bytes) -> None:
        if self._disposed or not self._connection_id:
            raise RuntimeError("The in-process runtime connection is closed.")
        ok = self._lib.connection_write(self._connection_id, frame, len(frame))
        if not ok:
            raise RuntimeError("Failed to write a frame to the in-process runtime connection.")

    def dispose(self) -> None:
        """Close the FFI connection, shut down the native host, release resources.

        Idempotent. Waits for any in-flight outbound callback to finish before
        dropping the callback reference to avoid a use-after-free.
        """
        with self._dispose_lock:
            if self._disposed:
                return
            self._disposed = True

        # Stop accepting new callbacks and wait for in-flight ones to drain.
        with self._callback_lock:
            pass  # _disposed is set; new callbacks bail out immediately.

View on GitHub (pinned to cd8cf15dc3)