{"record":{"id":"dcaa5abb21f029d9","repo":"oracle/graal","slug":"handshake-failed-unrecognized-message-from-the-d","errorCode":null,"errorMessage":"handshake failed - unrecognized message from the debugger","messagePattern":"handshake failed - unrecognized message from the debugger","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/HandshakeController.java","lineNumber":127,"sourceCode":"\n        byte[] b = new byte[hello.length];\n        int received = 0;\n        while (received < hello.length) {\n            int n;\n            try {\n                n = s.getInputStream().read(b, received, hello.length - received);\n            } catch (SocketTimeoutException x) {\n                throw new IOException(\"handshake timeout\");\n            }\n            if (n < 0) {\n                s.close();\n                throw new IOException(\"handshake failed - connection prematurely closed\");\n            }\n            received += n;\n        }\n        for (int i = 0; i < hello.length; i++) {\n            if (b[i] != hello[i]) {\n                throw new IOException(\"handshake failed - unrecognized message from the debugger\");\n            }\n        }\n\n        // handshake received, so return the gesture to establish the jdwp transport\n        s.getOutputStream().write(hello);\n        return true;\n    }\n}\n","sourceCodeStart":109,"sourceCodeEnd":136,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/HandshakeController.java#L109-L136","documentation":"Thrown by HandshakeController.handshake when the full 14 bytes arrived but do not equal the ASCII string 'JDWP-Handshake'. The JDWP wire protocol requires both peers to begin with exactly that greeting; any other bytes mean the peer is not speaking JDWP, and the agent refuses to continue.","triggerScenarios":"An HTTP client (curl), an SMTP/other protocol client, a binary protocol speaker, or a wrong-version tool connects to the JDWP socket and sends its own protocol banner, which then mismatches the expected greeting byte-for-byte.","commonSituations":"Debug port confused with the application's HTTP port; a reverse proxy in front of the JVM routing browser requests to the JDWP listener; scripts probing the port with arbitrary payloads.","solutions":["Point the JDWP client at the actual agent port (jdwp=... at the Espresso launcher, e.g. --jdb or agentlib-style flag) and the HTTP client at the app port","Do not put a generic reverse proxy in front of the JDWP listener","Log the offending first bytes to identify which client is misconnecting"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (IOException e) when message contains 'unrecognized message': log the first bytes received; do not retry the same peer automatically (it is not a JDWP client).","preventionTips":["Publish the JDWP port separately from HTTP/app ports","Never front the JDWP listener with an HTTP reverse proxy"],"tags":["espresso","jdwp","network","protocol-mismatch","handshake"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}