apache/flink · error · NullKeyFieldException
Unable to access field {} on object {}
Error message
Unable to access field {} on object {} What it means
PojoComparator.accessField(field, object) wraps Field.get(object); a NullPointerException from reflection occurs when 'object' itself is null, and Flink surfaces it as NullKeyFieldException('Unable to access field ... on object null'). It means the POJO whose key field is being read is null.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/PojoComparator.java:194
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void getFlatComparator(List<TypeComparator> flatComparators) {
for (int i = 0; i < comparators.length; i++) {
if (comparators[i] instanceof CompositeTypeComparator) {
((CompositeTypeComparator) comparators[i]).getFlatComparator(flatComparators);
} else {
flatComparators.add(comparators[i]);
}
}
}
/** This method is handling the IllegalAccess exceptions of Field.get() */
public final Object accessField(Field field, Object object) {
try {
object = field.get(object);
} catch (NullPointerException npex) {
throw new NullKeyFieldException(
"Unable to access field " + field + " on object " + object);
} catch (IllegalAccessException iaex) {
throw new RuntimeException(
"This should not happen since we call setAccesssible(true) in the ctor."
+ " fields: "
+ field
+ " obj: "
+ object);
}
return object;
}
@Override
public int hash(T value) {
int i = 0;
int code = 0;
for (; i < this.keyFields.length; i++) {
code *= TupleComparatorBase.HASH_SALT[i & 0x1F];View on GitHub (pinned to 2f3c205e92)
Solutions
- Filter out null records before keyBy/groupBy: stream.filter(Objects::nonNull)
- Fix the source/deserializer so it never emits null records (emit a Skip/retry or throw instead)
- Wrap records in a non-null container type (e.g. Tuple2 or a custom wrapper) if null must be representable
Example fix
// before d stream .keyBy(pojo -> pojo.getUserId()) ... // after d stream .filter(Objects::nonNull) .keyBy(pojo -> pojo.getUserId()) ...
Defensive patterns
Strategy: validation
Validate before calling
// guard before keyBy/groupBy on POJOs d stream .filter(Objects::nonNull) .keyBy(...);
Type guard
static <T> boolean isUsableAsPojoKey(T record) {
return record != null;
} Try / catch
try {
comparator.hash(record);
} catch (NullKeyFieldException e) {
// null record reached the comparator; upstream null leak
throw new IllegalArgumentException("Null record reached keyed operator", e);
} Prevention
- Never emit null top-level records from sources or flatMaps
- Filter nulls before keyed/sorted operators
- Represent absence with wrapper types instead of null records
When it happens
Trigger: groupBy/join/sort on a POJO key where a record in the stream/state is null: hash(), setReference(), compare(), etc. dereference keyFields[i] on a null POJO via accessField and reflection throws NPE.
Common situations: A source emits null elements (e.g. deserialization failure mapped to null, flatMap returning null); nulls inside keyed state or co-group inputs where POJO comparators are applied; inner nulls are fine but the top-level record being null is not.
Related errors
- Types that do not implement java.lang.Comparable cannot be u
- Comparator {} specifies an invalid length for the normalized
- Cannot copy serializer
- A NullPointerException occurred while accessing a key field
- {}
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/1f7d7585bd971657.
Report an issue: GitHub.