github/copilot-sdk · error · IllegalStateException
Session is not connected — RPC client is unavailable
Error message
Session is not connected — RPC client is unavailable
What it means
CopilotSession.getRpc() lazily publishes the SessionRpc handle from the rpc field; if rpc is still null the session has no live connection to the server and the method throws IllegalStateException instead of returning a unusable RPC client.
Solutions
- Await session readiness (e.g. a ready/initialized future) before calling getRpc()
- Catch IllegalStateException and retry after the session reconnects
- Check whether the session was terminated/reconnected; obtain a fresh CopilotSession if so
- Serialize access so session setup completes before worker threads call RPC APIs
Example fix
// before
session.getRpc().invoke("task", req);
// after
session.whenReady().join(); // ensure handshake done
session.getRpc().invoke("task", req); Defensive patterns
Strategy: try-catch
Validate before calling
if (!session.isConnected() /* or readiness future not done */) { awaitReadiness(session); } Try / catch
try {
session.getRpc();
} catch (IllegalStateException e) {
if (e.getMessage().contains("not connected")) {
// wait for reconnect or recreate the session
}
} Prevention
- Await session initialization before worker threads use session APIs
- Subscribe to lifecycle/disconnect events and stop issuing RPCs on disconnect
- Recreate sessions after connection loss instead of reusing stale objects
When it happens
Trigger: Calling session.getRpc() (directly or via metadata/task/setModel/sendMcpAuthResponse/interest/updateSessionOptionsForMode) before the session's initialization handshake finished or after the underlying connection dropped and rpc was nulled.
Common situations: Touching getRpc() immediately after createSession returns on a pre-registered/resumed session; using a session after connection loss; calling session APIs from a thread that races session initialization.
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
- No session found for sessionId
- session.create response did not include a sessionId
- session.create returned sessionId
- Client not connected; call start() first
- Failed to delete session
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/26ba82547cea84c8.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/CopilotSession.java:405
* Returns the typed RPC client for this session.
* <p>
* Provides strongly-typed access to all session-level API namespaces. The
* {@code sessionId} is injected automatically into every call.
* <p>
* Example usage:
*
* <pre>{@code
* var agents = session.getRpc().agent.list().get();
* }</pre>
*
* @return the session-scoped typed RPC client (never {@code null})
* @throws IllegalStateException
* if the session is not connected
* @since 1.0.0
*/
public SessionRpc getRpc() {
if (rpc == null) {
throw new IllegalStateException("Session is not connected — RPC client is unavailable");
}
SessionRpc current = sessionRpc;
if (current == null) {
synchronized (this) {
current = sessionRpc;
if (current == null) {
sessionRpc = current = new SessionRpc(rpc::invoke, sessionId);
}
}
}
return current;
}
/**
* Sets a custom error handler for exceptions thrown by event handlers.
* <p>
* When an event handler registered via {@link #on(Consumer)} or
* {@link #on(Class, Consumer)} throws an exception during event dispatch, theView on GitHub (pinned to cd8cf15dc3)