apache/iceberg · error · java.lang.IllegalArgumentException
Class %s does not implement DynamicRecordGeneratorSQL
Error message
Class %s does not implement DynamicRecordGeneratorSQL
What it means
Thrown when a configured DynamicRecordGeneratorSQL implementation class was loaded but its instances cannot be cast to the expected DynamicRecordGeneratorSQL interface, so Flink aborts sink construction. The buildChecked lookup matched a constructor shape, but the type does not implement the required interface.
Source
Thrown at flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/IcebergTableSink.java:293
.withProperties(tableProperties)
.create();
}
private DynamicTableRecordGenerator createDynamicRecordGenerator(String generatorImpl) {
RowType rowType = (RowType) resolvedSchema.toSourceRowDataType().getLogicalType();
DynConstructors.Ctor<DynamicTableRecordGenerator> ctor;
try {
ctor =
DynConstructors.builder(DynamicTableRecordGenerator.class)
.loader(IcebergTableSink.class.getClassLoader())
.impl(generatorImpl, RowType.class)
.impl(generatorImpl, RowType.class, Map.class, Configuration.class)
.buildChecked();
return ctor.newInstance(rowType, writeProps, fromReadableConfig());
} catch (ClassCastException e) {
throw new IllegalArgumentException(
String.format("Class %s does not implement DynamicRecordGeneratorSQL", generatorImpl), e);
} catch (Exception e) {
throw new RuntimeException(
String.format("Failed to instantiate DynamicRecordGeneratorSQL %s", generatorImpl), e);
}
}
private Configuration fromReadableConfig() {
return readableConfig instanceof Configuration
? (Configuration) readableConfig
: Configuration.fromMap(readableConfig.toMap());
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Make the configured class implement DynamicRecordGeneratorSQL (and its factory/ctor contract for RowType, Map, Configuration).
- Check for duplicate Iceberg jars in the Flink /lib and job jar; remove conflicting versions and rebuild shaded jar.
- Verify the fully-qualified class name in the property points to the intended implementation.
Example fix
// before
properties.setProperty("write.dynamic-record-generator", "com.example.MyGen"); // MyGen does not implement the interface
// after
public class MyGen implements DynamicRecordGeneratorSQL { ... } Defensive patterns
Strategy: validation
Validate before calling
Class<?> clz = Class.forName(generatorImpl, true, IcebergTableSink.class.getClassLoader());
if (!DynamicRecordGeneratorSQL.class.isAssignableFrom(clz)) {
throw new IllegalStateException(generatorImpl + " must implement DynamicRecordGeneratorSQL");
} Type guard
if (obj instanceof DynamicRecordGeneratorSQL) { DynamicRecordGeneratorSQL gen = (DynamicRecordGeneratorSQL) obj; } Prevention
- Implement the DynamicRecordGeneratorSQL interface on every custom generator.
- Keep one Iceberg version on the Flink classpath; avoid duplicate shaded jars.
- Unit-test instantiation of the configured generator class before submitting jobs.
When it happens
Trigger: Setting 'write-dynamic-record-generator' (or equivalent write property) to a class name that compiles a matching constructor but does not implement org.apache.iceberg.flink.DynamicRecordGeneratorSQL; classpath conflict where two versions of the interface exist and the impl implements the wrong one.
Common situations: Typo'd or hand-rolled generator class; stale jar on the classpath after upgrading Iceberg so the interface was repackaged/renamed; fat-jar shading putting DynamicRecordGeneratorSQL in two different classloaders.
Related errors
- Could not deserialize the WriteResult object
- Failed to deserialize the split.
- Failed to deserialize the split.
- 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/61e59aa1edcfe711.
Report an issue: GitHub.