apache/flink · error · InvalidProgramException
This type ({type}) cannot be used as key.
Error message
This type ({type}) cannot be used as key. What it means
Thrown by Keys.ExpressionKeys(String[], TypeInformation) in the else-branch where the data type is NOT a CompositeType. For atomic/non-composite types the entire type must itself satisfy isKeyType(). If the type is both non-composite and non-key-type (e.g. a raw Object, a byte array, or a GenericTypeInfo wrapping an unrecognised class), no key can be extracted at all.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/common/operators/Keys.java:343
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;
} else {
this.originalKeyTypes[i] = cType.getTypeAt(strippedKeyExpr);
}
}
} else {
if (!type.isKeyType()) {
throw new InvalidProgramException(
"This type (" + type + ") cannot be used as key.");
}
// check that all key expressions are valid
for (String keyExpr : keyExpressions) {
if (keyExpr == null) {
throw new InvalidProgramException("Expression key may not be null.");
}
// strip off whitespace
keyExpr = keyExpr.trim();
// check that full type is addressed
if (!(SELECT_ALL_CHAR.equals(keyExpr)
|| SELECT_ALL_CHAR_SCALA.equals(keyExpr))) {
throw new InvalidProgramException(
"Field expression must be equal to '"
+ SELECT_ALL_CHAR
+ "' or '"
+ SELECT_ALL_CHAR_SCALAView on GitHub (pinned to 2f3c205e92)
Solutions
- Wrap the value in a Tuple1 or a properly-annotated POJO and keyBy on the inner field.
- Supply a KeySelector<T, K> to keyBy that extracts a primitive/String key from the object.
- Register a TypeInfoFactory for the class so Flink recognises it as a composite or key type.
- Avoid using byte[], Object[], or raw generics as keys; convert to a comparable wrapper first.
Example fix
// before
DataStream<MyObject> ds = ...;
ds.keyBy("*"); // MyObject is GenericTypeInfo → not key type
// after
ds.keyBy(obj -> obj.getId()); // KeySelector extracting a String Defensive patterns
Strategy: type-guard
Validate before calling
if (!(type instanceof CompositeType) && !type.isKeyType()) {
throw new IllegalArgumentException(
"Type " + type + " is neither composite nor a valid key type.");
} Type guard
static boolean canBeUsedAsKey(TypeInformation<?> type) {
return (type instanceof CompositeType) || type.isKeyType();
} Try / catch
try {
new Keys.ExpressionKeys<>(exprs, type);
} catch (InvalidProgramException e) {
if (e.getMessage().contains("cannot be used as key")) {
ds.keyBy(new KeySelector<T,String>(){ ... });
} else throw e;
} Prevention
- Prefer KeySelector over field-expression keyBy for opaque/generic types.
- Register TypeInfoFactories for custom domain classes.
- Avoid using raw Object or arrays as stream element types.
When it happens
Trigger: Passing a non-composite, non-key type such as ObjectType, a raw array, or a GenericTypeInfo to ExpressionKeys with a field expression. Calling keyBy on a DataStream<SomeRandomClass> where SomeRandomClass is neither a recognised POJO/Tuple nor itself a valid key type.
Common situations: Using DataStream<MyDomainObject> without registering type info, so Flink treats it as GenericTypeInfo. Attempting to use an opaque/third-party Java object directly as a key without a KeySelector.
Related errors
- This type ({field.getType()}) cannot be used as key.
- Field expression must be equal to '*' or '_' for non-composi
- Specifying keys via field positions is only valid for tuple
- This type ({ffd.getType()}) cannot be used as key.
- Field expression must be equal to '*' or '_' for atomic type
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/0e47d8ee6ed31856.
Report an issue: GitHub.