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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions