Skip to content

KryoBufferUnderflowException #1265

@palexdev

Description

@palexdev

Describe the bug
I don't know if I'm missing something or what, but I get a com.esotericsoftware.kryo.io.KryoBufferUnderflowException whenever I exchange messages in a TCP client/server implementation.

To Reproduce

sealed interface Message permits Ping, Pong {}
record Ping(int id) implements Message {}
record Pong(int id, String info) implements Message {}

class KryoServer {
    private final int port;
    private ServerSocket serverSocket;
    private final ExecutorService pool = Executors.newCachedThreadPool();
    private volatile boolean running = false;
    private final Kryo kryo = new Kryo();

    { kryo.register(Ping.class); kryo.register(Pong.class); }

    KryoServer(int port) { this.port = port; }

    void start() throws IOException {
        serverSocket = new ServerSocket(port);
        running = true;
        System.out.println("[Server] listening on port " + port);
        while (running) {
            try {
                Socket client = serverSocket.accept();
                pool.execute(() -> handleClient(client));
            } catch (IOException e) {
                if (!running) break;
                throw e;
            }
        }
    }

    void stop() throws IOException {
        running = false;
        pool.shutdownNow();
        serverSocket.close();
    }

    private void handleClient(Socket socket) {
        try (socket;
             InputStream  in  = socket.getInputStream();
             OutputStream out = socket.getOutputStream()) {
            
            Input  kryoIn  = new Input(in,  4096);
            Output kryoOut = new Output(out, 4096);

            while (!Thread.currentThread().isInterrupted()) {
                Message msg = (Message) kryo.readClassAndObject(kryoIn);
                System.out.println("[Server] received: " + msg);

                if (msg instanceof Ping ping) {
                    Pong reply = new Pong(ping.id(), "pong-ok");
                    kryo.writeClassAndObject(kryoOut, reply);
                    kryoOut.flush();
                    System.out.println("[Server] sent: " + reply);
                }
            }
        } catch (IOException e) {
            System.out.println("[Server] client disconnected: " + e.getMessage());
        }
    }
}

class KryoClient {
    private final String host;
    private final int    port;
    private final Kryo   kryo = new Kryo();

    { kryo.register(Ping.class); kryo.register(Pong.class); }

    KryoClient(String host, int port) { this.host = host; this.port = port; }

    void exchange(int n) throws IOException, InterruptedException {
        try (Socket socket = new Socket(host, port);
             InputStream  in  = socket.getInputStream();
             OutputStream out = socket.getOutputStream()) {

            Input  kryoIn  = new Input(in,  4096);
            Output kryoOut = new Output(out, 4096);

            for (int i = 1; i <= n; i++) {
                kryo.writeClassAndObject(kryoOut, new Ping(i));
                kryoOut.flush();
                System.out.println("[Client] inviato: Ping[id=" + i + "]");

                Pong pong = (Pong) kryo.readClassAndObject(kryoIn);
                System.out.println("[Client] ricevuto: " + pong);

                Thread.sleep(100);
            }
        }
    }
}

// --- Test main ---
public class TestKryoTCP {
    static void main() throws Exception {
        KryoServer server = new KryoServer(9000);
        KryoClient client = new KryoClient("localhost", 9000);

        Thread srvThread = new Thread(() -> {
            try { server.start(); }
            catch (IOException e) { throw new RuntimeException(e); }
        });
        srvThread.setDaemon(true);
        srvThread.start();

        Thread.sleep(200);
        client.exchange(5);
        server.stop();
        srvThread.join(1000);
    }
}

Environment:

  • OS: Windows 11
  • JDK Version: 25
  • Kryo Version: 5.6.2

Additional context
Initially, I started implementing my TCP client and server classes reading the documentation on the internet, I never dealt with such things. Although I could and can read messages correctly, I get the aforementioned exception.
I also tried asking AI, and it generated from scratch a test class that presents the exact same issue, that's the reproducer I shared. I see that this is not the first time such bug appears on this repo, but it's never been looking at for lacking a repro(?), so here you go. Hope it's enough to tell me if I'm doing something wrong or if it's a hidden bug of the library

Edit: I don't have such issue if I use KryoNet instead. Unfortunately the lib doesn't seem to be supported anymore, and it's such a shame considering that it seems to be the only one offering a simple high-level API for such things.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions