github/copilot-sdk · error

The in-process runtime connection is closed.

Error message

The in-process runtime connection is closed.

What it means

writeFrame refuses to send when the FFI runtime connection has already been disposed or never received a connection id. The library throws this instead of silently dropping frames to the native runtime, since any write after teardown would be lost or crash the native layer. It surfaces from the host constructor while it frames the initial handshake message.

Solutions

  1. Create a fresh FfiRuntimeHost/session instead of reusing the disposed one.
  2. Check that no code path calls dispose() before the first frame (e.g. an error handler or finally block firing early).
  3. Verify the native runtime loaded and connection setup succeeded before writing; inspect earlier errors from connection setup.
  4. Add a disposed/isConnected check before reusing host references in long-lived code.

Example fix

// before
const host = new FfiRuntimeHost(lib);
await session.close();
await sendFrame(host, frame); // throws

// after
const host = new FfiRuntimeHost(lib);
await sendFrame(host, frame);
await session.close();
Defensive patterns

Strategy: try-catch

Validate before calling

if (host.disposed) throw new Error('host already disposed; create a new one');

Type guard

function isHostUsable(h: { disposed: boolean; connectionId?: unknown }): boolean { return !h.disposed && !!h.connectionId; }

Try / catch

try {
  host.writeFrame(frame);
} catch (e) {
  if (e.message.includes('connection is closed')) {
    host = createNewHost(); // recreate instead of retrying on a disposed host
  } else throw e;
}

Prevention

When it happens

Trigger: Calling any framed operation (including the FfiRuntimeHost constructor's initial write) after host.dispose() ran, or when connectionWrite setup failed so this.connectionId was never assigned.

Common situations: Double-constructing/tearing down a session, reusing a host object after close, a failed native connectionWrite during initialization, or keeping a stale reference to a closed runtime host.

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/247ac256d2dd85c5. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/ffiRuntimeHost.ts:244

            0
        );
        if (!this.connectionId) {
            this.unregisterCallback();
            this.lib.hostShutdown(this.serverId);
            this.serverId = 0;
            throw new Error("copilot_runtime_connection_open failed.");
        }

        // The in-process transport has no socket/pipe handle to keep the Node event loop
        // alive while the SDK is idle awaiting a server→client frame. koffi delivers the
        // outbound callback on the loop but does not reference it, so hold one referenced
        // timer for the lifetime of the connection.
        this.keepAliveTimer = setInterval(() => {}, KEEP_ALIVE_INTERVAL_MS);
    }

    private writeFrame(frame: Buffer): void {
        if (this.disposed || !this.connectionId) {
            throw new Error("The in-process runtime connection is closed.");
        }
        const ok = this.lib.connectionWrite(this.connectionId, frame, frame.length);
        if (!ok) {
            throw new Error("Failed to write a frame to the in-process runtime connection.");
        }
    }

    /**
     * Native outbound (server→client) callback. koffi delivers it on the JS event loop
     * via a threadsafe function, so the frame is decoded and written straight to
     * {@link receiveStream}. The native pointer is only valid for this call, so the
     * bytes are copied out before returning.
     */
    private feedInbound(bytesPtr: unknown, bytesLen: number | bigint): void {
        // An exception thrown across the native→JS (Node-API) boundary cannot propagate
        // and would surface only as a DEP0168 "uncaught Node-API callback exception"
        // warning, so catch and log it here instead of letting it escape.
        try {

View on GitHub (pinned to cd8cf15dc3)