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

  1. Choose a field whose type is a valid key type (primitive, String, Date, or a Tuple/POJO of key types).
  2. If the desired field is complex, use a KeySelector to extract a hashable sub-field.
  3. 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

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


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/c0d8ce897a40e1ab. Report an issue: GitHub.