github/copilot-sdk · error · IllegalStateException
Session is closed
Error message
Session is closed
What it means
CopilotSession tracks termination internally; once close() has been invoked, every subsequent API call passes through ensureNotTerminated(), which throws IllegalStateException('Session is closed'). The SDK does this to prevent RPC dispatch on a disposed transport and to make use-after-close explicit rather than hanging or failing obscurely.
Solutions
- Check session state (or track closed status yourself) before invoking session methods.
- Restructure shutdown so all in-flight work completes or is cancelled before close() is called.
- Recreate a new CopilotSession if continued operation is required after termination.
- Synchronize session usage so callbacks cannot run concurrently with close().
Example fix
// before session.close(); session.setModel(options); // IllegalStateException // after session.close(); session = createNewSession(); session.setModel(options);
Defensive patterns
Strategy: try-catch
Validate before calling
if (sessionClosed) {
return CompletableFuture.failedFuture(new IllegalStateException("session already closed"));
} Type guard
boolean isUsable(CopilotSession s) { return s != null && !s.isClosed(); } Try / catch
try {
session.setModel(options);
} catch (IllegalStateException e) {
LOG.warning("session closed, re-creating: " + e.getMessage());
session = createNewSession();
session.setModel(options);
} Prevention
- Guard all callbacks and async tasks against session shutdown.
- Close the session only after cancelling or awaiting in-flight work.
- Track session lifecycle centrally instead of sharing the reference across owners.
When it happens
Trigger: Any CopilotSession public method (e.g. setModel, send requests) called after session.close(), or when a shutdown/termination sequence already ran (CopilotSession.java:2510).
Common situations: Async callbacks or listeners that outlive the session fire after close(); lifecycle races where a timeout handler closes the session while another thread still submits work; double-close followed by reuse.
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
- FfiRuntimeHost has already been started.
- CLI process not started
- CLI child process was unexpectedly started in parent…
- Server port not available
- Session not found
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/19ef0d0c668339bd.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/CopilotSession.java:2510
* @return a future that completes when compaction finishes
* @throws IllegalStateException
* if this session has been terminated
* @since 1.0.11
*/
public CompletableFuture<Void> compact() {
ensureNotTerminated();
return rpc.invoke("session.compaction.compact", Map.of("sessionId", sessionId), Void.class);
}
/**
* Verifies that this session has not yet been terminated.
*
* @throws IllegalStateException
* if close() has already been invoked
*/
private void ensureNotTerminated() {
if (isTerminated) {
throw new IllegalStateException("Session is closed");
}
}
/**
* Disposes the session and releases all associated resources.
* <p>
* This destroys the session on the server, clears all event handlers, and
* releases tool and permission handlers. After calling this method, the session
* cannot be used again. Subsequent calls to this method have no effect.
*/
@Override
public void close() {
synchronized (this) {
if (isTerminated) {
return; // Already terminated - no-op
}
isTerminated = true;
}View on GitHub (pinned to cd8cf15dc3)