apache/flink · critical · IOException

Unsupported version: {}

Error message

Unsupported version: {}

What it means

NativeS3RecoverableSerializer.deserialize rejects any serialized form whose version differs from CURRENT_VERSION. The serializer is SimpleVersionedSerializer-based: Flink stores a version number next to the bytes, and this implementation supports only its exact current layout, so mismatched versions are treated as unrecoverable corruption rather than attempted migration.

Source

Thrown at flink-filesystems/flink-s3-fs-native/src/main/java/org/apache/flink/fs/s3native/writer/NativeS3RecoverableSerializer.java:109

            out.writeInt(part.getPartNumber());
            out.writeUTF(part.getETag());
        }
        String incompleteObject = recoverable.incompleteObjectName();
        if (incompleteObject == null) {
            out.writeBoolean(false);
        } else {
            out.writeBoolean(true);
            out.writeUTF(incompleteObject);
            out.writeLong(recoverable.incompleteObjectLength());
        }
        out.flush();
        return outputStream.toByteArray();
    }

    @Override
    public NativeS3Recoverable deserialize(int version, byte[] serialized) throws IOException {
        if (version != CURRENT_VERSION) {
            throw new IOException("Unsupported version: " + version);
        }
        ByteArrayInputStream inputStream = new ByteArrayInputStream(serialized);
        DataInputStream in = new DataInputStream(inputStream);
        String objectName = in.readUTF();
        String uploadId = in.readUTF();
        long numBytesInParts = in.readLong();
        int numParts = in.readInt();
        List<PartETag> parts = new ArrayList<>(numParts);
        for (int i = 0; i < numParts; i++) {
            int partNumber = in.readInt();
            String eTag = in.readUTF();
            parts.add(new PartETag(partNumber, eTag));
        }
        boolean hasIncompletePart = in.readBoolean();
        String incompleteObjectName = null;
        long incompleteObjectLength = -1;
        if (hasIncompletePart) {
            incompleteObjectName = in.readUTF();

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Pin one consistent version of the flink-s3-fs-native plugin across the job that wrote the checkpoint and the job restoring it.
  2. If you must move forward, discard the incompatible checkpoint/savepoint and restart the streaming source from an explicit reset point (resume behavior, not exact state).
  3. Verify the plugin jar in lib/ matches the Flink distribution version (plugin shading must match the connector build).
  4. If you control the code, add migration paths in deserialize() for prior versions instead of hard-failing.
Defensive patterns

Strategy: validation

Validate before calling

// before restoring, verify plugin/state provenance
int v = NativeS3RecoverableSerializer.INSTANCE.getVersion();
// ensure the checkpoint was written by a job using the same plugin version

Prevention

When it happens

Trigger: Restoring a checkpoint/savepoint that was written by a different (older or newer) version of flink-s3-fs-native whose NativeS3RecoverableSerializer layout differs; any manual corruption of the serialized bytes' version header.

Common situations: Upgrading or downgrading the flink-s3-fs-native plugin jar without discarding incompatible state; mixing plugin versions between the checkpoint-writing job and the restoring job; loading a savepoint taken with an internal build; stale state from before a serializer format change.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/c2e0b569f5652d3b. Report an issue: GitHub.