apache/iceberg · error · UncheckedIOException
Failed to deserialize sort key sketch
Error message
Failed to deserialize sort key sketch
What it means
Thrown by SortKeySketchSerializer when deserializing a SortKey sketch from its byte representation fails with an IOException during Flink state/network transfer of shuffle statistics. It wraps the underlying serialization failure as an UncheckedIOException. The library throws it because sort-key sketches are exchanged between Flink subtasks and a corrupt or incompatible payload cannot be decoded.
Solutions
- Align the Iceberg version used to write the payload and the one deserializing it; restore checkpoints with the same version that produced them.
- Check SortKeySerializer version compatibility; if restoring from a lower version, switch the serializer to v1 as StatisticsUtil does via changeSortKeySerializerVersion(1).
- Re-run the shuffle from a fresh job (discard the old state) so sketches are rebuilt from input data.
- Inspect the cause (IOException) for corruption/truncation and re-transfer or re-serialize the sketch.
Example fix
// before: deserializing bytes written by an older serializer SortKey[] keys = serializer.deserializeFromMemory(record); // after: guard version before deserializing (as StatisticsUtil does) serializer.changeSortKeySerializerVersion(1); SortKey[] keys = serializer.deserializeFromMemory(record); serializer.changeSortKeySerializerVersionLatest();
Defensive patterns
Strategy: try-catch
Validate before calling
if (bytes == null || bytes.length == 0) { throw new IllegalArgumentException("Empty sketch payload"); } Type guard
boolean isSketchPayload(byte[] bytes) { return bytes != null && bytes.length > 0; } Try / catch
try {
SortKey[] keys = serializer.deserializeFromMemory(record);
} catch (UncheckedIOException e) {
LOG.warn("Sketch deserialization failed, rebuilding statistics", e);
// fall back to re-collecting statistics
} Prevention
- Keep the same Iceberg version for writing and reading checkpoints/statistics payloads.
- Use the v1 fallback path (changeSortKeySerializerVersion) when restoring older state.
- Never hand-edit or truncate serialized statistics bytes.
When it happens
Trigger: Calling deserializeFromMemory on bytes written by an incompatible SortKeySerializer version, truncated or corrupted Flink state / operator-channel payload, or reading a snapshot/checkpoint saved by a different Iceberg/Flink version whose sketch layout changed.
Common situations: Restoring a Flink job from a checkpoint/savepoint produced with an older Iceberg version, network-serialized statistics crossing serializer versions after an upgrade, or manual replay of statistics payloads stored out of band.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Fail to deserialize data statistics
- Fail to serialize aggregated statistics
- Fail to serialize aggregated statistics
- Fail to serialize aggregated statistics
- Fail to serialize data statistics
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/7607d56732a996b2.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/shuffle/SortKeySketchSerializer.java:113
long offset = startingOffset;
Util.checkBounds(offset, Integer.BYTES, mem.getCapacity());
int numBytes = mem.getInt(offset);
offset += Integer.BYTES;
Util.checkBounds(offset, numBytes, mem.getCapacity());
byte[] sortKeyBytes = new byte[numBytes];
mem.getByteArray(offset, sortKeyBytes, 0, numBytes);
input.setBuffer(sortKeyBytes);
try {
List<SortKey> sortKeys = listSerializer.deserialize(input);
SortKey[] array = new SortKey[numItems];
sortKeys.toArray(array);
input.releaseArrays();
return array;
} catch (IOException e) {
throw new UncheckedIOException("Failed to deserialize sort key sketch", e);
}
}
@Override
public int sizeOf(SortKey item) {
return serializeToByteArray(item).length;
}
@Override
public int sizeOf(Memory mem, long offset, int numItems) {
Preconditions.checkArgument(mem != null, "Invalid input memory: null");
if (numItems <= 0) {
return 0;
}
Util.checkBounds(offset, Integer.BYTES, mem.getCapacity());
int numBytes = mem.getInt(offset);
return Integer.BYTES + numBytes;View on GitHub (pinned to 86d9c8fc54)