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
- Call start() only once per FfiRuntimeHost instance; guard with an isStarted()/state check before calling.
- If restart is needed, call close() first and create a new FfiRuntimeHost instance.
- 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
- Initialize the host in exactly one lifecycle method
- Use an AtomicBoolean/enum state machine around start()
- Never call start() from retry logic without recreating the host
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
- An in-process FFI runtime library is already loaded from
- The in-process runtime connection is closed.
- Session is closed
- FfiRuntimeHost is already closed.
- copilot_runtime_host_start failed (library '').
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)