github/copilot-sdk · error · RuntimeException
User input handler error
Error message
User input handler error
What it means
CopilotSession wraps a user-input handler invocation and converts any exception thrown by the handler (or its returned future) into a RuntimeException with the message 'User input handler error', keeping the original as the cause. The session logs the original exception at SEVERE before rethrowing. This is a wrapper error: the real problem is in the user-supplied handler registered via the SDK.
Solutions
- Inspect the 'cause' of the logged exception and fix the bug inside your UserInputHandler.handle implementation.
- Wrap your handler body in defensive try/catch and return a failed future with a descriptive exception so diagnostics are clearer.
- Validate request payloads before acting on them inside the handler (null checks on request fields).
Example fix
// before
handler = (request, invocation) -> {
return process(request.text().trim()); // NPE if text() null
};
// after
handler = (request, invocation) -> {
if (request.text() == null) {
return CompletableFuture.failedFuture(new IllegalArgumentException("text is required"));
}
return process(request.text().trim());
}; Defensive patterns
Strategy: try-catch
Validate before calling
if (request.text() == null || request.text().isBlank()) {
return CompletableFuture.failedFuture(new IllegalArgumentException("user input text is required"));
} Type guard
boolean hasText(UserInputRequest r) { return r != null && r.text() != null && !r.text().isBlank(); } Try / catch
try {
return handler.handle(request, invocation);
} catch (Exception e) {
LOG.log(Level.SEVERE, "user input handler failed", e);
return CompletableFuture.failedFuture(e);
} Prevention
- Null-check every request field before use inside the handler.
- Return failed futures with descriptive exceptions instead of letting raw NPEs escape.
- Log with context (session id, request id) to make wrapped causes traceable.
When it happens
Trigger: Any callback handler registered for user input requests (UserInputHandler.handle) throws synchronously, or the CompletableFuture it returns completes exceptionally (e.g. in CopilotSession.java:1783 during request processing).
Common situations: A developer's user-input handler dereferences a null field from the request, performs I/O that fails, or returns a future that fails asynchronously; the session's RPC dispatcher then surfaces the failure under this generic message.
Related errors
- Exit plan mode handler error
- Auto mode switch handler error
- workingDirectory is not supported with…
- env is not supported with RuntimeConnection.forInProcess()…
- No session found for sessionId
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/ceb5af79f835ffac.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/CopilotSession.java:1783
* Handles a user input request from the Copilot CLI.
* <p>
* Called internally when the server requests user input.
*
* @param request
* the user input request
* @return a future that resolves with the user input response
*/
CompletableFuture<UserInputResponse> handleUserInputRequest(UserInputRequest request) {
UserInputHandler handler = userInputHandler.get();
if (handler == null) {
return CompletableFuture.failedFuture(new IllegalStateException("No user input handler registered"));
}
try {
var invocation = new UserInputInvocation().setSessionId(sessionId);
return handler.handle(request, invocation).exceptionally(ex -> {
LOG.log(Level.SEVERE, "User input handler threw an exception", ex);
throw new RuntimeException("User input handler error", ex);
});
} catch (Exception e) {
LOG.log(Level.SEVERE, "Failed to process user input request", e);
return CompletableFuture.failedFuture(e);
}
}
/**
* 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();View on GitHub (pinned to cd8cf15dc3)