apache/flink · error · RuntimeException
Could not find record canonical constructor
Error message
Could not find record canonical constructor
What it means
Thrown by JavaRecordBuilderFactory.create() when the reflective lookup of a record's canonical constructor fails. The lookup calls Class.getRecordComponents() reflectively and clazz.getDeclaredConstructor(componentTypes); any failure (class is not a record, JDK too old, inaccessible class, constructor signature mismatch) is wrapped in this RuntimeException.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/JavaRecordBuilderFactory.java:168
field == null ? -1 : componentNames.indexOf(fields[i].getName());
}
// We have to initialize newly added primitive fields to their correct default value
Object[] defaultValues = new Object[componentNames.size()];
for (int i = 0; i < componentNames.size(); i++) {
Class<?> fieldType = componentTypes[i];
boolean newPrimitive =
fieldType.isPrimitive()
&& !previousFields.contains(componentNames.get(i));
defaultValues[i] = newPrimitive ? Defaults.defaultValue(fieldType) : null;
}
return new JavaRecordBuilderFactory<>(
recordConstructor, argIndexMapping, defaultValues);
} else {
return new JavaRecordBuilderFactory<>(recordConstructor);
}
} catch (Exception e) {
throw new RuntimeException("Could not find record canonical constructor", e);
}
}
}
View on GitHub (pinned to 2f3c205e92)
Solutions
- Verify the class is a real record: Class.isRecord() (and run on Java 16+ for full record support)
- Check the cause chain - a NoSuchMethodException means component types in the canonical constructor no longer match getRecordComponents() output
- Ensure the record's package is opened/exported to Flink's user-code classloader under JPMS
- If the type is not meant to be a record, fix type extraction so POJO serialization is used instead
Example fix
// before
Object factory = JavaRecordBuilderFactory.create(someClass, fields);
// after
if (!someClass.isRecord()) {
throw new IllegalArgumentException(someClass + " is not a record");
}
Object factory = JavaRecordBuilderFactory.create(someClass, fields); Defensive patterns
Strategy: validation
Validate before calling
if (!clazz.isRecord()) {
throw new IllegalArgumentException(clazz + " is not a record; use POJO serialization");
}
// also requires Java 16+ runtime
if (!Class.class.getMethods().toString().isEmpty() && Runtime.version().feature() < 16) {
throw new IllegalStateException("Records require Java 16+");
} Type guard
static boolean isRecordClass(Class<?> c) {
return c != null && c.isRecord();
} Try / catch
try {
factory = JavaRecordBuilderFactory.create(clazz, fields);
} catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot build record factory for " + clazz + ": " + e.getCause(), e);
} Prevention
- Run user code containing records on Java 16 or newer
- Open the record's package to Flink's classloader under JPMS
- Keep record component names/order aligned with serialized field lists during schema evolution
When it happens
Trigger: Calling create(clazz, fields) where clazz is not a java.lang.Record (e.g. a POJO or a class compiled to an interface via -parameters quirks); running on a JVM that lacks record support (< Java 16); record components changed so getDeclaredConstructor(componentTypes) finds no exact match; record/classloader inaccessibility despite setAccessible(true).
Common situations: TypeInformation mistakenly classified a plain class as a record; running Flink user code on Java 11 where getRecordComponents does not exist; modules/JPMS denies deep reflection on the record package; upgrading a record's component types breaks restore from an old serializer snapshot.
Related errors
- Could not instantiate record
- Cannot deserialize and unwrap accumulators properly.
- Failed to deserialize coordination response
- Cannot deserialize and unwrap accumulators properly.
- Unable to instantiate Hadoop InputSplit
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/074074a416a104a6.
Report an issue: GitHub.