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
- Create a fresh FfiRuntimeHost/session instead of reusing the disposed one.
- Check that no code path calls dispose() before the first frame (e.g. an error handler or finally block firing early).
- Verify the native runtime loaded and connection setup succeeded before writing; inspect earlier errors from connection setup.
- 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
- Track host lifecycle in one owner; never share disposed hosts.
- Set host references to null after dispose() to surface stale usage fast.
- Await construction/initialization fully before issuing frames.
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
- The in-process runtime connection is closed.
- An in-process FFI runtime library is already loaded from
- Failed to write a frame to the in-process runtime…
- factory.run and factory.resume are not allowed while a…
- FfiRuntimeHost is already closed.
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)