{"record":{"id":"247ac256d2dd85c5","repo":"github/copilot-sdk","slug":"the-in-process-runtime-connection-is-closed","errorCode":null,"errorMessage":"The in-process runtime connection is closed.","messagePattern":"The in-process runtime connection is closed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/ffiRuntimeHost.ts","lineNumber":244,"sourceCode":"            0\n        );\n        if (!this.connectionId) {\n            this.unregisterCallback();\n            this.lib.hostShutdown(this.serverId);\n            this.serverId = 0;\n            throw new Error(\"copilot_runtime_connection_open failed.\");\n        }\n\n        // The in-process transport has no socket/pipe handle to keep the Node event loop\n        // alive while the SDK is idle awaiting a server→client frame. koffi delivers the\n        // outbound callback on the loop but does not reference it, so hold one referenced\n        // timer for the lifetime of the connection.\n        this.keepAliveTimer = setInterval(() => {}, KEEP_ALIVE_INTERVAL_MS);\n    }\n\n    private writeFrame(frame: Buffer): void {\n        if (this.disposed || !this.connectionId) {\n            throw new Error(\"The in-process runtime connection is closed.\");\n        }\n        const ok = this.lib.connectionWrite(this.connectionId, frame, frame.length);\n        if (!ok) {\n            throw new Error(\"Failed to write a frame to the in-process runtime connection.\");\n        }\n    }\n\n    /**\n     * Native outbound (server→client) callback. koffi delivers it on the JS event loop\n     * via a threadsafe function, so the frame is decoded and written straight to\n     * {@link receiveStream}. The native pointer is only valid for this call, so the\n     * bytes are copied out before returning.\n     */\n    private feedInbound(bytesPtr: unknown, bytesLen: number | bigint): void {\n        // An exception thrown across the native→JS (Node-API) boundary cannot propagate\n        // and would surface only as a DEP0168 \"uncaught Node-API callback exception\"\n        // warning, so catch and log it here instead of letting it escape.\n        try {","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/ffiRuntimeHost.ts#L226-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst host = new FfiRuntimeHost(lib);\nawait session.close();\nawait sendFrame(host, frame); // throws\n\n// after\nconst host = new FfiRuntimeHost(lib);\nawait sendFrame(host, frame);\nawait session.close();","handlingStrategy":"try-catch","validationCode":"if (host.disposed) throw new Error('host already disposed; create a new one');","typeGuard":"function isHostUsable(h: { disposed: boolean; connectionId?: unknown }): boolean { return !h.disposed && !!h.connectionId; }","tryCatchPattern":"try {\n  host.writeFrame(frame);\n} catch (e) {\n  if (e.message.includes('connection is closed')) {\n    host = createNewHost(); // recreate instead of retrying on a disposed host\n  } else throw e;\n}","preventionTips":["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."],"tags":["ffi","connection-closed","lifecycle","nodejs"],"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"}