{"record":{"id":"6c01b26bf4b6d407","repo":"apache/flink","slug":"this-type-field-gettype-cannot-be-used-as-ke","errorCode":null,"errorMessage":"This type ({field.getType()}) cannot be used as key.","messagePattern":"This type \\((.+?)\\) cannot be used as key\\.","errorType":"exception","errorClass":"InvalidProgramException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/common/operators/Keys.java","lineNumber":327,"sourceCode":"                    if (keyExpr == null) {\n                        throw new InvalidProgramException(\"Expression key may not be null.\");\n                    }\n                    // strip off whitespace\n                    keyExpr = keyExpr.trim();\n\n                    List<FlatFieldDescriptor> flatFields = cType.getFlatFields(keyExpr);\n\n                    if (flatFields.size() == 0) {\n                        throw new InvalidProgramException(\n                                \"Unable to extract key from expression '\"\n                                        + keyExpr\n                                        + \"' on key \"\n                                        + cType);\n                    }\n                    // check if all nested fields can be used as keys\n                    for (FlatFieldDescriptor field : flatFields) {\n                        if (!field.getType().isKeyType()) {\n                            throw new InvalidProgramException(\n                                    \"This type (\" + field.getType() + \") cannot be used as key.\");\n                        }\n                    }\n                    // add flat fields to key fields\n                    keyFields.addAll(flatFields);\n\n                    String strippedKeyExpr = WILD_CARD_REGEX.matcher(keyExpr).replaceAll(\"\");\n                    if (strippedKeyExpr.isEmpty()) {\n                        this.originalKeyTypes[i] = type;\n                    } else {\n                        this.originalKeyTypes[i] = cType.getTypeAt(strippedKeyExpr);\n                    }\n                }\n            } else {\n                if (!type.isKeyType()) {\n                    throw new InvalidProgramException(\n                            \"This type (\" + type + \") cannot be used as key.\");\n                }","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/common/operators/Keys.java#L309-L345","documentation":"Thrown by Keys.ExpressionKeys(String[], TypeInformation) when a nested field of a composite type (Tuple/POJO/Case Class) resolves to a type that is not a key type. isKeyType() returns false for non-hashable types such as POJOs, GenericTypeInfo (opaque Java objects), arrays, or types without a proper TypeComparator. Every field that participates in a key must be individually hashable and comparable so the runtime can partition and sort by it.","triggerScenarios":"Calling keyBy(\"fieldName\") on a DataStream whose POJO has a field of a non-key type (e.g. a nested POJO, a byte[], or a raw Object). Also triggered by constructing new ExpressionKeys<>(new String[]{\"nestedField\"}, type) where the resolved FlatFieldDescriptor reports isKeyType()==false.","commonSituations":"Grouping or joining on a POJO field that itself contains another POJO or a collection. Migrating from a Tuple of primitives to a richer domain object and forgetting that only leaf-level key types are allowed. Using a GenericTypeInfo-backed field (e.g. a third-party class Flink cannot introspect) as a key.","solutions":["Flatten the problematic field into individual primitive/String sub-fields and keyBy on those instead.","Provide a KeySelector that extracts only the hashable leaf fields from the nested type.","Annotate the nested class as a valid POJO or register a TypeInfoFactory so Flink can decompose it into key-type leaves.","If the field is genuinely a single opaque comparable, wrap it in a type Flink recognises (e.g. a Tuple1) or implement a custom TypeInfoFactory that exposes isKeyType()==true."],"exampleFix":"// before\nds.keyBy(\"address\")  // address is a POJO → not a key type\n\n// after\nds.keyBy(\"address.zipCode\", \"address.street\")  // primitive String fields","handlingStrategy":"validation","validationCode":"// Before keyBy, check each field is a key type\nCompositeType<?> ct = (CompositeType<?>) typeInfo;\nList<FlatFieldDescriptor> flat = ct.getFlatFields(\"fieldName\");\nfor (FlatFieldDescriptor ffd : flat) {\n    if (!ffd.getType().isKeyType()) {\n        throw new IllegalArgumentException(\n            \"Field resolves to non-key type: \" + ffd.getType());\n    }\n}","typeGuard":"static boolean isFieldKeyType(CompositeType<?> type, String expr) {\n    List<FlatFieldDescriptor> flat = type.getFlatFields(expr);\n    if (flat.isEmpty()) return false;\n    return flat.stream().allMatch(f -> f.getType().isKeyType());\n}","tryCatchPattern":"try {\n    ds.keyBy(\"fieldName\");\n} catch (InvalidProgramException e) {\n    if (e.getMessage().contains(\"cannot be used as key\")) {\n        // fall back to KeySelector extracting leaf fields\n        ds.keyBy(record -> extractLeafKey(record));\n    } else throw e;\n}","preventionTips":["Key only on primitive, String, or other isKeyType()-true leaf fields.","Avoid keying on nested POJOs, arrays, or GenericTypeInfo fields.","Unit-test keyBy calls with representative type information."],"tags":["keys","type-system","keyby","flink-core"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}