apache/flink · error · InvalidProgramException
Unable to extract key from expression '{keyExpr}' on key {cT
Error message
Unable to extract key from expression '{keyExpr}' on key {cType} What it means
Thrown by ExpressionKeys(String[], TypeInformation) when cType.getFlatFields(keyExpr) returns an empty list, i.e. the expression does not match any field on the composite type. The expression may be misspelled, reference a non-existent nested path, or target a field not present in the POJO/Tuple schema.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/common/operators/Keys.java:318
if (type instanceof CompositeType) {
CompositeType<T> cType = (CompositeType<T>) type;
this.originalKeyTypes = new TypeInformation<?>[keyExpressions.length];
// extract the keys on their flat position
for (int i = 0; i < keyExpressions.length; i++) {
String keyExpr = keyExpressions[i];
if (keyExpr == null) {
throw new InvalidProgramException("Expression key may not be null.");
}
// strip off whitespace
keyExpr = keyExpr.trim();
List<FlatFieldDescriptor> flatFields = cType.getFlatFields(keyExpr);
if (flatFields.size() == 0) {
throw new InvalidProgramException(
"Unable to extract key from expression '"
+ keyExpr
+ "' on key "
+ cType);
}
// check if all nested fields can be used as keys
for (FlatFieldDescriptor field : flatFields) {
if (!field.getType().isKeyType()) {
throw new InvalidProgramException(
"This type (" + field.getType() + ") cannot be used as key.");
}
}
// add flat fields to key fields
keyFields.addAll(flatFields);
String strippedKeyExpr = WILD_CARD_REGEX.matcher(keyExpr).replaceAll("");
if (strippedKeyExpr.isEmpty()) {
this.originalKeyTypes[i] = type;View on GitHub (pinned to 2f3c205e92)
Solutions
- Verify the field name exactly matches the POJO/Tuple field (case-sensitive for POJOs).
- For nested fields, confirm each path segment exists and is correctly named.
- Print the type's field names (cType.getFieldNames()) to compare against your expression.
Example fix
// before (POJO field is 'userId')
ds.groupBy("usrId");
// after
ds.groupBy("userId"); Defensive patterns
Strategy: validation
Validate before calling
List<FlatFieldDescriptor> resolved = cType.getFlatFields(keyExpr);
if (resolved.isEmpty()) {
throw new IllegalArgumentException(
"No field matches expression '" + keyExpr + "' on " + cType + ". Known fields: "
+ Arrays.toString(cType.getFieldNames()));
} Type guard
static boolean fieldExists(CompositeType<?> t, String expr) {
try { return !t.getFlatFields(expr).isEmpty(); }
catch (Throwable x) { return false; }
} Prevention
- Match the exact field name (case-sensitive for POJOs).
- Print cType.getFieldNames() to compare against your expression when unsure.
- For nested paths, confirm each segment exists before composing the expression.
When it happens
Trigger: Calling groupBy("usrId") instead of groupBy("userId"); referencing a nested path like "address.zip" on a POJO whose address field has no zip; using a field name valid on one schema but not the runtime type.
Common situations: Typos in field-expression keys; schema drift where a field was renamed or removed; copy-pasting a key from a different dataset's schema; case-sensitivity mismatches on POJO field names.
Related errors
- Tuple size must be greater than 0. Size: {type.getArity()}
- The grouping fields must not be empty.
- This type ({ffd.getType()}) cannot be used as key.
- Expression key may not be null.
- Return type {keyType} of KeySelector {keyExtractor.getClass(
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/0f7d24a6a2767e61.
Report an issue: GitHub.