github/copilot-sdk · error · IllegalStateException

FfiRuntimeHost was closed during startup.

Error message

FfiRuntimeHost was closed during startup.

What it means

After the native host started, start() re-checks the disposed flag while holding operationLock. If close() ran concurrently while hostStart was blocking, the library shuts down the just-created native host (best effort) and throws, because publishing handles into a closed host would leak them.

Solutions

  1. Ensure close() is not invoked until after start() completes; serialize lifecycle with the same lock or a startup Future.
  2. Check return value / exceptions of start() before calling close() in shutdown paths.
  3. Orchestrate lifecycle in a single owner component.
  4. Catch IllegalStateException and treat as benign during shutdown.

Example fix

// before
new Thread(() -> client.close()).start();
client.start(entrypoint, options); // may race
// after
client.start(entrypoint, options);
Runtime.getRuntime().addShutdownHook(new Thread(client::close));
Defensive patterns

Strategy: try-catch

Try / catch

try { host.start(e, o); } catch (IllegalStateException ex) { if (shuttingDown.get()) { log.info("startup raced with shutdown, ignoring"); return; } throw ex; }

Prevention

When it happens

Trigger: One thread calls start() while another calls close() on the same FfiRuntimeHost; close() sets disposed before start finishes its blocking native call.

Common situations: Application shutdown hook racing with initialization; a timeout-based watchdog closing the client while startup is still in flight; double-managed lifecycle (container + app both closing).

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/9abb92a2e69edb2e. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/ffi/FfiRuntimeHost.java:124

        byte[] argvJson = buildArgvJson(entrypointPath, options);
        byte[] envJson = buildEnvJson(options);
        int hostHandle = runHostStartOnBlockingThread(argvJson, envJson);
        if (hostHandle == 0) {
            String lib = libraryPath != null ? libraryPath : "<unknown>";
            throw new IllegalStateException("copilot_runtime_host_start failed (library '" + lib + "').");
        }

        // Hold operationLock while publishing handles to serialize with close().
        // Recheck disposed in case close() ran while hostStart was blocking.
        operationLock.lock();
        try {
            if (disposed.get()) {
                try {
                    nativeBinding.hostShutdown(hostHandle);
                } catch (Throwable ignored) {
                    // Best effort
                }
                throw new IllegalStateException("FfiRuntimeHost was closed during startup.");
            }
            serverId.set(hostHandle);

            OutboundCallback callback = createOutboundCallback();
            callbackRef = callback;
            int connHandle = nativeBinding.connectionOpen(hostHandle, callback, Pointer.NULL, null, 0, null, 0, null,
                    0);
            if (connHandle == 0) {
                try {
                    nativeBinding.hostShutdown(hostHandle);
                } catch (Throwable ignored) {
                    // Best effort
                }
                serverId.set(0);
                callbackRef = null;
                throw new IllegalStateException("copilot_runtime_connection_open failed.");
            }
            connectionId.set(connHandle);

View on GitHub (pinned to cd8cf15dc3)