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

  1. Inspect the 'cause' of the logged exception and fix the bug inside your UserInputHandler.handle implementation.
  2. Wrap your handler body in defensive try/catch and return a failed future with a descriptive exception so diagnostics are clearer.
  3. 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

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


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)