github/copilot-sdk · error · IllegalStateException

FfiRuntimeHost has already been started.

Error message

FfiRuntimeHost has already been started.

What it means

FfiRuntimeHost.start() enforces single-start semantics: if the host handle (serverId) or connection handle (connectionId) is already non-zero, the host has been started previously and a second start would leak native resources. The library throws IllegalStateException immediately without touching native code.

Solutions

  1. Call start() only once per FfiRuntimeHost instance; guard with an isStarted()/state check before calling.
  2. If restart is needed, call close() first and create a new FfiRuntimeHost instance.
  3. Refactor to lazy initialization so only one code path invokes start().

Example fix

// before
host.start(entrypoint, options);
host.start(entrypoint, options); // throws
// after
if (!hostStarted) {
    host.start(entrypoint, options);
    hostStarted = true;
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (hostStarted.get()) { throw new IllegalStateException("already started"); }
host.start(entrypoint, options); hostStarted.set(true);

Type guard

boolean isStarted(FfiRuntimeHost h) { return h != null && h.getState() != FfiRuntimeHost.State.NEW; }

Try / catch

try { host.start(e, o); } catch (IllegalStateException ex) { if (!ex.getMessage().contains("already been started")) throw ex; }

Prevention

When it happens

Trigger: Calling start(entrypointPath, options) twice on the same FfiRuntimeHost instance without close() in between; e.g. openInProcessTransport re-invoking start after a prior successful start, or application retry logic calling start again after it already succeeded.

Common situations: Framework code that creates the client on multiple lifecycle events (servlet init + first request), a retry wrapper that re-calls start after an unrelated error, or accidental shared singleton host reused by two components each calling start().

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

Appendix: source

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

    }

    /**
     * Starts the in-process runtime and opens a connection.
     *
     * @param entrypointPath
     *            optional explicit legacy CLI entrypoint passed in
     *            {@code argv_json}
     * @param options
     *            client options used to construct {@code argv_json} and
     *            {@code env_json}
     */
    public void start(String entrypointPath, CopilotClientOptions options) {
        Objects.requireNonNull(options, "options must not be null");
        if (disposed.get()) {
            throw new IllegalStateException("FfiRuntimeHost is already closed.");
        }
        if (serverId.get() != 0 || connectionId.get() != 0) {
            throw new IllegalStateException("FfiRuntimeHost has already been started.");
        }

        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) {

View on GitHub (pinned to cd8cf15dc3)