apache/iceberg · critical · RuntimeException
Failed to deserialize the split.
Error message
Failed to deserialize the split.
What it means
IcebergSourceSplit.deserializeV1 wraps a ClassNotFoundException thrown while Java-deserializing the split payload into RuntimeException with the message 'Failed to deserialize the split.' It means the split bytes were written by a class version not present on the deserializing task's classpath.
Source
Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/source/split/IcebergSourceSplit.java:131
.add("length", fileScanTask.length())
.toString())
.collect(Collectors.toList()));
}
byte[] serializeV1() throws IOException {
if (serializedBytesCache == null) {
serializedBytesCache = InstantiationUtil.serializeObject(this);
}
return serializedBytesCache;
}
static IcebergSourceSplit deserializeV1(byte[] serialized) throws IOException {
try {
return InstantiationUtil.deserializeObject(
serialized, IcebergSourceSplit.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to deserialize the split.", e);
}
}
byte[] serializeV2() throws IOException {
return serialize(2);
}
byte[] serializeV3() throws IOException {
return serialize(3);
}
private byte[] serialize(int version) throws IOException {
if (serializedBytesCache == null) {
DataOutputSerializer out = SERIALIZER_CACHE.get();
Collection<FileScanTask> fileScanTasks = task.tasks();
Preconditions.checkArgument(
fileOffset >= 0 && fileOffset < fileScanTasks.size(),
"Invalid file offset: %s. Should be within the range of [0, %s)",View on GitHub (pinned to 86d9c8fc54)
Solutions
- Deploy the identical iceberg-flink-runtime jar to all nodes and match the version that serialized the savepoint.
- Ensure the jar is part of the user code (uber jar) rather than only on the JobManager classpath.
- Verify shaded class relocation didn't change between versions; use the officially released flink-runtime artifact.
- If recovery isn't possible, restart the job without state and let splits be re-planned.
Example fix
// before: jar only on JM flink-conf classloader: parent-first with JM-only iceberg jar // after: ship jar with the job flink run -c Main job.jar // iceberg-flink-runtime bundled inside job.jar
Defensive patterns
Strategy: validation
Validate before calling
// verify class availability in the task environment before restore
Class.forName("org.apache.iceberg.flink.source.split.IcebergSourceSplit",
true, Thread.currentThread().getContextClassLoader()); Prevention
- Bundle iceberg-flink-runtime inside the user job jar so it ships to every TaskManager.
- Keep connector versions identical to the checkpoint-writing version.
- Verify shade relocations are stable across your custom builds.
- Smoke-test savepoint restore in CI before production upgrades.
When it happens
Trigger: deserializeV1 receives bytes whose embedded class can't be resolved against the task's classloader — e.g. restore with a different Iceberg flink-runtime jar, shaded-class relocation differences, or user-jar not shipped to all TaskManagers.
Common situations: Upgrading/downgrading the connector while resuming from savepoint; per-job vs lib deployment mismatch where the Iceberg jar exists on the JobManager but not TaskManagers; shade plugin relocated classes differently between versions.
Related errors
- Failed to deserialize the split.
- Could not deserialize the WriteResult object
- Could not deserialize the WriteResult object
- Could not deserialize the WriteResult object
- Failed to deserialize the split.
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/3b99ebc7e7dbd7db.
Report an issue: GitHub.