github/copilot-sdk · error · IllegalStateException

Client not connected; call start() first

Error message

Client not connected; call start() first

What it means

getRpc() returns the ServerRpc handle only after start() has successfully established a connection. If the connection future is null, still pending, or completed exceptionally, the client has no RPC channel and IllegalStateException is thrown.

Solutions

  1. Call start() and await its completion before getRpc(): client.start().join() (or thenAccept)
  2. Check the exception cause of the failed connection future (start().exceptionally) — likely server launch failure
  3. Gate all RPC usage behind a readiness flag set after start() completes

Example fix

// before
ServerRpc rpc = client.getRpc();
// after
client.start().join();
ServerRpc rpc = client.getRpc();
Defensive patterns

Strategy: validation

Validate before calling

if (client.getConnectionFuture() == null || !client.getConnectionFuture().isDone()) {
    client.start().join(); // or await readiness signal
}

Try / catch

try {
    ServerRpc rpc = client.getRpc();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("not connected")) {
        client.start().join();
    }
}

Prevention

When it happens

Trigger: Calling client.getRpc() before start(), while start()'s connection handshake is still in flight, or after connection failed (future completed exceptionally).

Common situations: Forgetting start() in new integration code; racing getRpc() immediately after start() without awaiting the returned future; server binary failed to launch so the connection never completes.

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

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/CopilotClient.java:1456

     * Provides strongly-typed access to all server-level API namespaces such as
     * {@code models}, {@code tools}, {@code account}, and {@code mcp}.
     * <p>
     * Example usage:
     *
     * <pre>{@code
     * client.start().get();
     * var models = client.getRpc().models.list().get();
     * }</pre>
     *
     * @return the server-level typed RPC client
     * @throws IllegalStateException
     *             if the client is not connected; call {@link #start()} first
     * @since 1.0.0
     */
    public ServerRpc getRpc() {
        CompletableFuture<Connection> future = connectionFuture;
        if (future == null || !future.isDone() || future.isCompletedExceptionally()) {
            throw new IllegalStateException("Client not connected; call start() first");
        }
        return future.join().serverRpc();
    }

    /**
     * Pings the server to check connectivity.
     * <p>
     * This can be used to verify that the server is responsive and to check the
     * protocol version.
     *
     * @param message
     *            an optional message to echo back
     * @return a future that resolves with the ping response
     * @see PingResponse
     */
    public CompletableFuture<PingResponse> ping(String message) {
        return ensureConnected().thenCompose(connection -> connection.rpc.invoke("ping",
                Map.of("message", message != null ? message : ""), PingResponse.class));

View on GitHub (pinned to cd8cf15dc3)