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

  1. Deploy the identical iceberg-flink-runtime jar to all nodes and match the version that serialized the savepoint.
  2. Ensure the jar is part of the user code (uber jar) rather than only on the JobManager classpath.
  3. Verify shaded class relocation didn't change between versions; use the officially released flink-runtime artifact.
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/3b99ebc7e7dbd7db. Report an issue: GitHub.