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

  1. Check session state (or track closed status yourself) before invoking session methods.
  2. Restructure shutdown so all in-flight work completes or is cancelled before close() is called.
  3. Recreate a new CopilotSession if continued operation is required after termination.
  4. 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

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


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)