apache/flink · error · InvalidProgramException
This type ({ffd.getType()}) cannot be used as key.
Error message
This type ({ffd.getType()}) cannot be used as key. What it means
Thrown by ExpressionKeys(int[], TypeInformation, boolean) when a flattened key field's type fails isKeyType(). Even when the type is a valid tuple, each selected field must itself be a hashable/comparable key type; a field whose flattened type is a Map, List, Object, or a non-key composite cannot be used as a key.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/common/operators/Keys.java:282
// extract key field types
CompositeType<T> cType = (CompositeType<T>) type;
this.keyFields = new ArrayList<>(type.getTotalFields());
// for each key position, find all (nested) field types
String[] fieldNames = cType.getFieldNames();
this.originalKeyTypes = new TypeInformation<?>[keyPositions.length];
ArrayList<FlatFieldDescriptor> tmpList = new ArrayList<>();
for (int i = 0; i < keyPositions.length; i++) {
int keyPos = keyPositions[i];
tmpList.clear();
// get all flat fields
this.originalKeyTypes[i] = cType.getTypeAt(keyPos);
cType.getFlatFields(fieldNames[keyPos], 0, tmpList);
// check if fields are of key type
for (FlatFieldDescriptor ffd : tmpList) {
if (!ffd.getType().isKeyType()) {
throw new InvalidProgramException(
"This type (" + ffd.getType() + ") cannot be used as key.");
}
}
this.keyFields.addAll(tmpList);
}
}
/** Create String-based (nested) field expression keys on a composite type. */
public ExpressionKeys(String keyExpression, TypeInformation<T> type) {
this(new String[] {keyExpression}, type);
}
/** Create String-based (nested) field expression keys on a composite type. */
public ExpressionKeys(String[] keyExpressions, TypeInformation<T> type) {
checkNotNull(keyExpressions, "Field expression cannot be null.");
this.keyFields = new ArrayList<>(keyExpressions.length);
View on GitHub (pinned to 2f3c205e92)
Solutions
- Choose a field whose type is a valid key type (primitive, String, Date, or a Tuple/POJO of key types).
- If the desired field is complex, use a KeySelector to extract a hashable sub-field.
- Flatten nested structures upstream so the key field is a scalar.
Example fix
// before (field 2 is Map<String,Integer>)
ds.groupBy(2);
// after — extract a scalar via a KeySelector
ds.keyBy(e -> e.f2.get("tenantId")); Defensive patterns
Strategy: type-guard
Validate before calling
for (FlatFieldDescriptor ffd : flatFields) {
if (!ffd.getType().isKeyType()) {
throw new IllegalArgumentException("Field type " + ffd.getType() + " cannot be used as key");
}
} Type guard
static boolean isKeyable(TypeInformation<?> t) { return t != null && t.isKeyType(); } Prevention
- Key only on scalar/primitive/String/Tuple-of-key-types fields.
- If the desired field is a collection or nested object, extract a scalar via a KeySelector.
- Flatten nested structures upstream so the key field is hashable and comparable.
When it happens
Trigger: Calling groupBy(2) where tuple field 2 is a Map<String,String>; keyBy(1) where field 1 is a List; selecting a nested POJO field that is itself a non-key composite.
Common situations: Keying on a field that holds a collection or nested object; schema changes that turned a scalar field into a complex type; selecting a Tuple field that wraps a non-key type.
Related errors
- Specifying keys via field positions is only valid for tuple
- Tuple size must be greater than 0. Size: {type.getArity()}
- The grouping fields must not be empty.
- Expression key may not be null.
- Unable to extract key from expression '{keyExpr}' on key {cT
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/c0d8ce897a40e1ab.
Report an issue: GitHub.