oracle/graal · error · IOException

handshake failed - connection prematurely closed

Error message

handshake failed - connection prematurely closed

What it means

Thrown by HandshakeController.handshake when the socket stream returns EOF (read() < 0) while the 14-byte 'JDWP-Handshake' greeting is still incomplete. The peer closed the connection partway through (or immediately after connecting) after sending fewer than 14 bytes, so the agent closes the socket and fails the handshake.

Source

Thrown at espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/HandshakeController.java:121

    /**
     * Handshake with the debugger.
     */
    static boolean handshake(Socket s) throws IOException {

        byte[] hello = JDWP_HANDSHAKE.getBytes(StandardCharsets.UTF_8);

        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

  1. Confirm the peer is a real JDWP client and stays connected through the handshake
  2. Exclude the JDWP port from TCP health checks and port scans
  3. Check intermediary idle-timeout/reset behavior between debugger and target JVM
Defensive patterns

Strategy: try-catch

Try / catch

catch (IOException e) when message contains 'prematurely closed': treat as a non-debugger probe; close cleanly and continue the accept loop.

Prevention

When it happens

Trigger: A client connects, sends a partial greeting (or nothing), then closes the socket; also middleboxes that accept then reset connections, or a debugger killed between connect and handshake.

Common situations: Docker/Kubernetes port checks that open and immediately close connections; SYN-proxying firewalls; an IDE attaching and being cancelled mid-attach; keepalive/timeout settings on an intermediary killing idle new connections.

Understand the failure class

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/42d3d7b4703c0cd8. Report an issue: GitHub.