apache/iceberg · error · java.lang.IllegalArgumentException
Class %s does not implement DynamicRecordGeneratorSQL
Error message
Class %s does not implement DynamicRecordGeneratorSQL
What it means
IcebergTableSink.createDynamicRecordGenerator loads a user-supplied DynamicRecordGeneratorSQL implementation via reflection and casts it to the expected interface; if the configured class does not implement DynamicRecordGeneratorSQL, the ClassCastException is rethrown as this IllegalArgumentException with the class name.
Source
Thrown at flink/v2.1/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
- Verify the configured class implements org.apache.iceberg.flink.DynamicRecordGeneratorSQL with both (RowType) and (RowType, Map, Configuration) constructors
- Check the classpath for stale/multiple Iceberg versions (mvn dependency:tree) and align versions
- Correct the fully-qualified class name in the write property
Example fix
// before
write.dynamic-record-generator=com.example.MyGen (implements old interface)
// after
write.dynamic-record-generator=com.example.MyGen implements DynamicRecordGeneratorSQL {
MyGen(RowType t); MyGen(RowType t, Map<String,String> props, Configuration c);
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(generatorImpl);
if (!DynamicRecordGeneratorSQL.class.isAssignableFrom(c)) {
throw new IllegalArgumentException(generatorImpl + " must implement DynamicRecordGeneratorSQL");
} Try / catch
try {
generator = sink.createDynamicRecordGenerator(rowType);
} catch (IllegalArgumentException e) {
LOG.error("Generator {} does not implement DynamicRecordGeneratorSQL; check classpath/versions", generatorImpl, e);
throw e;
} Prevention
- Keep one Iceberg version on the Flink classpath (check dependency:tree)
- Verify the interface with instanceof in the generator's own tests
- Use the exact fully-qualified class name from the same Iceberg build
When it happens
Trigger: Setting the dynamic record generator property (write.dynamic-record-generator or equivalent write property) to a class name that exists but implements a different interface or an older/newer interface version.
Common situations: Copy-pasting a generator class name from another Iceberg version; classpath pollution with duplicate/old Iceberg jars providing a different DynamicRecordGeneratorSQL; typo'd fully-qualified class name resolving to the wrong class.
Related errors
- Cannot initialize MetricsReporter, %s does not implement Met
- Cannot initialize LockManager, missing no-arg constructor: %
- Cannot initialize LockManager, %s does not implement LockMan
- Class %s does not implement DynamicRecordGeneratorSQL
- Failed to instantiate DynamicRecordGeneratorSQL %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/ef81f5630d98e2b6.
Report an issue: GitHub.