EsotericSoftware / EsotericSoftware/kryo
Deserialization fails with "Buffer underflow" after add a new field to record class (Issue with backward compatibility)
- Dominant language
- HTML
- Stars
- 6.5k
- Forks
- 841
- Avg merge
- 39m
- Merged PRs (30d)
- 4
Description
**Kryo version 5.5.0**
**Java 17**
When data was serialized and after that a new field was added, deserilization fails with exception: `com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow`
**Stacktrace**
```
Exception in thread "main" com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.
Serialization trace:
info (akta.dcs.session.live.hash.Data)
at com.esotericsoftware.kryo.kryo5.io.Input.require(Input.java:221)
at com.esotericsoftware.kryo.kryo5.io.Input.readVarIntFlag(Input.java:482)
at com.esotericsoftware.kryo.kryo5.io.Input.readString(Input.java:777)
at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:174)
at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:164)
at com.esotericsoftware.kryo.kryo5.Kryo.readObjectOrNull(Kryo.java:826)
at com.esotericsoftware.kryo.kryo5.serializers.RecordSerializer.read(RecordSerializer.java:136)
at com.esotericsoftware.kryo.kryo5.Kryo.readObject(Kryo.java:777)
```
**Code example:**
```
public record Data(
String id
){}
===============================================
var kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
return kryo;
});
var data = new Data("1");
var output = new Output(1024, -1);
kryoLocal.get().writeObject(output, data);
output.flush();
var serializedData = output.toBytes(); // [-126, 49]
output.close();
```
After serialized, new field was added:
```
public record Data(
String id,
String info
){}
=================
var input = new Input(new byte[] {-126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();
```
And eventually an exception is thrown: `com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.`
But if convert _Java record_ to class, everything works properly:
**Code example:**
```
public class Data {
private String id;
public Data() {
}
public Data(
String id
) {
this.id = id;
}
public String id() {
return id;
}
}
=====================================
var kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
return kryo;
});
var data = new Data("1");
var output = new Output(1024, -1);
kryoLocal.get().writeObject(output, data);
output.flush();
var serializedData = output.toBytes(); // [1, 105, -28, 3, -126, 49]
output.close();
```
After serialized, new field was added:
```
public class Data {
private String id;
private String info;
public Data() {
}
public Data(
String id,
String info
) {
this.id = id;
this.info = info;
}
public String id() {
return id;
}
public String info() {
return info;
}
}
==========================================
var input = new Input(new byte[] {1, 105, -28, 3, -126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();
```
As we can see, that serialized **record** and **class** have different bytes.
Contributor guide
Assessment
This issue has not been assessed yet.