{"record":{"id":"ceb5af79f835ffac","repo":"github/copilot-sdk","slug":"user-input-handler-error","errorCode":null,"errorMessage":"User input handler error","messagePattern":"User input handler error","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/CopilotSession.java","lineNumber":1783,"sourceCode":"     * Handles a user input request from the Copilot CLI.\n     * <p>\n     * Called internally when the server requests user input.\n     *\n     * @param request\n     *            the user input request\n     * @return a future that resolves with the user input response\n     */\n    CompletableFuture<UserInputResponse> handleUserInputRequest(UserInputRequest request) {\n        UserInputHandler handler = userInputHandler.get();\n        if (handler == null) {\n            return CompletableFuture.failedFuture(new IllegalStateException(\"No user input handler registered\"));\n        }\n\n        try {\n            var invocation = new UserInputInvocation().setSessionId(sessionId);\n            return handler.handle(request, invocation).exceptionally(ex -> {\n                LOG.log(Level.SEVERE, \"User input handler threw an exception\", ex);\n                throw new RuntimeException(\"User input handler error\", ex);\n            });\n        } catch (Exception e) {\n            LOG.log(Level.SEVERE, \"Failed to process user input request\", e);\n            return CompletableFuture.failedFuture(e);\n        }\n    }\n\n    /**\n     * Handles an exit-plan-mode request from the Copilot CLI.\n     * <p>\n     * Called internally when the server sends an {@code exitPlanMode.request}.\n     *\n     * @param request\n     *            the exit-plan-mode request\n     * @return a future that resolves with the user's decision\n     */\n    CompletableFuture<ExitPlanModeResult> handleExitPlanModeRequest(ExitPlanModeRequest request) {\n        ExitPlanModeHandler handler = exitPlanModeHandler.get();","sourceCodeStart":1765,"sourceCodeEnd":1801,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/CopilotSession.java#L1765-L1801","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\nhandler = (request, invocation) -> {\n    return process(request.text().trim()); // NPE if text() null\n};\n// after\nhandler = (request, invocation) -> {\n    if (request.text() == null) {\n        return CompletableFuture.failedFuture(new IllegalArgumentException(\"text is required\"));\n    }\n    return process(request.text().trim());\n};","handlingStrategy":"try-catch","validationCode":"if (request.text() == null || request.text().isBlank()) {\n    return CompletableFuture.failedFuture(new IllegalArgumentException(\"user input text is required\"));\n}","typeGuard":"boolean hasText(UserInputRequest r) { return r != null && r.text() != null && !r.text().isBlank(); }","tryCatchPattern":"try {\n    return handler.handle(request, invocation);\n} catch (Exception e) {\n    LOG.log(Level.SEVERE, \"user input handler failed\", e);\n    return CompletableFuture.failedFuture(e);\n}","preventionTips":["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."],"tags":["handler-exception","rpc","user-input"],"backgroundTag":"handler-exception","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}