github/copilot-sdk · error · RuntimeException
Exit plan mode handler error
Error message
Exit plan mode handler error
What it means
CopilotSession invokes the registered exit-plan-mode handler and converts any synchronous or asynchronous failure in that handler into a RuntimeException with the message 'Exit plan mode handler error', logging the original at SEVERE. The failure originates in the user-supplied handler, not the SDK transport itself.
Solutions
- Read the cause of the wrapped exception and fix the failing logic in your ExitPlanModeHandler implementation.
- Guard state access in the handler against a session that may already be closing or terminated.
- Return a failed future with a domain-specific exception instead of letting raw exceptions propagate for better logs.
Example fix
// before
(request, invocation) -> saveState(invocation.getSessionId(), plan); // throws if plan null
// after
(request, invocation) -> {
if (plan == null) {
return CompletableFuture.completedFuture(null);
}
return saveState(invocation.getSessionId(), plan);
}; Defensive patterns
Strategy: try-catch
Validate before calling
if (plan == null || invocation.getSessionId() == null) {
return CompletableFuture.completedFuture(null);
} Type guard
boolean canPersist(Plan plan) { return plan != null && plan.steps() != null; } Try / catch
try {
return handler.handle(request, invocation);
} catch (Exception e) {
LOG.warning("exit plan mode failed: " + e.getMessage());
return CompletableFuture.failedFuture(e);
} Prevention
- Assume the session may be closing when the handler runs; guard state access.
- Keep persistence logic in the handler fault-tolerant (best-effort writes).
- Test your handler with a session close racing the plan-exit flow.
When it happens
Trigger: The handler registered for ExitPlanModeInvocation (ExitPlanModeHandler.handle) throws, or the CompletableFuture returned from handle() completes exceptionally (CopilotSession.java:1810).
Common situations: An exit-plan-mode handler tries to persist plan state to disk or a database and the write fails, or it accesses session state that has already been torn down when the user exits plan mode.
Related errors
- User input handler error
- Auto mode switch handler error
- invalid exit plan mode request payload
- workingDirectory is not supported with…
- env is not supported with RuntimeConnection.forInProcess()…
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/c5f4ebe28bbaacfb.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/CopilotSession.java:1810
* Handles an exit-plan-mode request from the Copilot CLI.
* <p>
* Called internally when the server sends an {@code exitPlanMode.request}.
*
* @param request
* the exit-plan-mode request
* @return a future that resolves with the user's decision
*/
CompletableFuture<ExitPlanModeResult> handleExitPlanModeRequest(ExitPlanModeRequest request) {
ExitPlanModeHandler handler = exitPlanModeHandler.get();
if (handler == null) {
return CompletableFuture.completedFuture(new ExitPlanModeResult().setApproved(true));
}
try {
var invocation = new ExitPlanModeInvocation().setSessionId(sessionId);
return handler.handle(request, invocation).exceptionally(ex -> {
LOG.log(Level.SEVERE, "Exit plan mode handler threw an exception", ex);
throw new RuntimeException("Exit plan mode handler error", ex);
});
} catch (Exception e) {
LOG.log(Level.SEVERE, "Failed to process exit plan mode request", e);
return CompletableFuture.failedFuture(e);
}
}
/**
* Handles an auto-mode-switch request from the Copilot CLI.
* <p>
* Called internally when the server sends an {@code autoModeSwitch.request}.
*
* @param request
* the auto-mode-switch request
* @return a future that resolves with the user's decision
*/
CompletableFuture<AutoModeSwitchResponse> handleAutoModeSwitchRequest(AutoModeSwitchRequest request) {
AutoModeSwitchHandler handler = autoModeSwitchHandler.get();View on GitHub (pinned to cd8cf15dc3)