{"record":{"id":"fc1c618e2fc83f84","repo":"oracle/graal","slug":"unable-to-handshake-with-debugger","errorCode":null,"errorMessage":"Unable to handshake with debugger","messagePattern":"Unable to handshake with debugger","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java","lineNumber":139,"sourceCode":"            this.latch = latch;\n        }\n\n        @Override\n        public void run() {\n            // first, complete the connection setup which is potentially blocking\n            DebuggerConnection debuggerConnection;\n            try {\n                Socket connectionSocket;\n                if (setupState.socket != null) {\n                    connectionSocket = setupState.socket;\n                } else { // we know we have a server socket then\n                    assert setupState.serverSocket != null;\n                    // this blocks until a debugger connects\n                    connectionSocket = setupState.serverSocket.accept();\n                }\n                // OK, ready to do the handshake with debugger\n                if (!HandshakeController.handshake(connectionSocket)) {\n                    throw new IOException(\"Unable to handshake with debugger\");\n                }\n                try {\n                    if (controller.isClosing()) {\n                        return;\n                    }\n                    // The following block has to be synchronized with resetting, so that\n                    // we can abandon further work in case we're told to tear down\n                    controller.getResettingLock().lockInterruptibly();\n                    // re-check to return immediately if closing\n                    if (controller.isClosing()) {\n                        return;\n                    }\n                    SocketConnection socketConnection = new SocketConnection(connectionSocket);\n                    debuggerConnection = new DebuggerConnection(socketConnection, controller);\n                    controller.setDebuggerConnection(debuggerConnection);\n                    controller.getEventListener().setConnection(socketConnection);\n                    if (!controller.isSuspend()) {\n                        // Fire the vm started event for the suspend=n case.","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/DebuggerConnection.java#L121-L157","documentation":"Thrown by DebuggerConnection when HandshakeController.handshake(socket) returns false after a debugger connection was accepted. The JDWP transport begins with both sides exchanging the 14-byte ASCII string 'JDWP-Handshake'; if the gesture cannot be completed, the connection setup aborts with this IOException. In practice handshake() either throws a specific sub-case (timeout, premature close, unrecognized message) or returns true, so this message marks a handshake failure that produced no finer diagnosis.","triggerScenarios":"Connecting to the Espresso JDWP agent's socket with a client that is not a JDWP debugger (curl, telnet, a health-check probe), or a debugger that drops or never completes the initial handshake exchange.","commonSituations":"Kubernetes/TCP load-balancer health probes hitting the JDWP port instead of the app port; connecting jdb/IDE to the wrong port; a debugger version speaking a nonstandard initial exchange.","solutions":["Verify only a real JDWP client (jdb -attach, IntelliJ/VS Code Java debugger) connects to the agent port","Move health checks to a different port so they never touch the JDWP listener","If it persists, capture the exact handshake bytes with a proxy to see what the peer sends"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (IOException e) when message contains 'Unable to handshake': log the peer address, close the socket, and re-enter the accept loop; real debuggers can reconnect.","preventionTips":["Reserve the JDWP port for debugger traffic only","Expose JDWP on loopback or an authenticated tunnel, not on shared service ports"],"tags":["espresso","jdwp","debugging","network","handshake"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}