oracle/graal · error · IOException
handshake failed - unrecognized message from the debugger
Error message
handshake failed - unrecognized message from the debugger
What it means
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.
Source
Thrown at espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/HandshakeController.java:127
byte[] b = new byte[hello.length];
int received = 0;
while (received < hello.length) {
int n;
try {
n = s.getInputStream().read(b, received, hello.length - received);
} catch (SocketTimeoutException x) {
throw new IOException("handshake timeout");
}
if (n < 0) {
s.close();
throw new IOException("handshake failed - connection prematurely closed");
}
received += n;
}
for (int i = 0; i < hello.length; i++) {
if (b[i] != hello[i]) {
throw new IOException("handshake failed - unrecognized message from the debugger");
}
}
// handshake received, so return the gesture to establish the jdwp transport
s.getOutputStream().write(hello);
return true;
}
}
View on GitHub (pinned to a66e9ccd1d)
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
Defensive patterns
Strategy: try-catch
Try / catch
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).
Prevention
- Publish the JDWP port separately from HTTP/app ports
- Never front the JDWP listener with an HTTP reverse proxy
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Unable to handshake with debugger
- handshake timeout
- handshake failed - connection prematurely closed
- Invalid JDWP option value: {key} can be only 'y' or 'n'.
- JDWP options must be a comma separated list of key=value pai
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/dcaa5abb21f029d9.
Report an issue: GitHub.