apache/flink · error · InvalidTypesException

The type {} cannot be used as a key.

Error message

The type {} cannot be used as a key.

What it means

BasicTypeInfo.createComparator returns a TypeComparator only when comparatorClass is non-null. Some basic types (e.g., Void) have a null comparatorClass because they are not orderable, so attempting to build a comparator for them throws InvalidTypesException saying the type cannot be used as a key. This is the key-eligibility gate for basic types.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/common/typeinfo/BasicTypeInfo.java:251

    @PublicEvolving
    public boolean isKeyType() {
        return true;
    }

    @Override
    @PublicEvolving
    public TypeSerializer<T> createSerializer(SerializerConfig serializerConfig) {
        return this.serializer;
    }

    @Override
    @PublicEvolving
    public TypeComparator<T> createComparator(
            boolean sortOrderAscending, ExecutionConfig executionConfig) {
        if (comparatorClass != null) {
            return instantiateComparator(comparatorClass, sortOrderAscending);
        } else {
            throw new InvalidTypesException(
                    "The type " + clazz.getSimpleName() + " cannot be used as a key.");
        }
    }

    // --------------------------------------------------------------------------------------------

    @Override
    public int hashCode() {
        return (31 * Objects.hash(clazz, serializer, comparatorClass))
                + Arrays.hashCode(possibleCastTargetTypes);
    }

    @Override
    public boolean canEqual(Object obj) {
        return obj instanceof BasicTypeInfo;
    }

    @Override

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Choose a key field whose TypeInformation.isKeyType() returns true (orderable types like String, Integer, Long).
  2. Avoid keying on Void or other non-orderable basic types; restructure the pipeline to use a concrete comparable key.
  3. Guard generic comparator-building code with typeInfo.isKeyType() before calling createComparator.

Example fix

// before
dataStream.keyBy(e -> null); // Void key -> throws

// after
dataStream.keyBy(Event::getId); // Long key, orderable
Defensive patterns

Strategy: type-guard

Validate before calling

TypeInformation<?> keyTi = TypeInformation.of(keyClass);
if (!keyTi.isKeyType()) {
    throw new IllegalArgumentException(keyTi + " cannot be used as a key");
}

Type guard

static boolean isUsableAsKey(TypeInformation<?> ti) {
    return ti.isKeyType();
}

Try / catch

// Prefer keyBy with a properly typed key selector; avoid catching createComparator.
// If building comparators generically:
try {
    return ti.createComparator(asc, cfg);
} catch (InvalidTypesException e) {
    throw new IllegalArgumentException(ti + " is not key-comparable", e);
}

Prevention

When it happens

Trigger: Using a basic type with no comparator as a key (keyBy, joining, grouping, windowing on that field); calling createComparator directly on a BasicTypeInfo whose comparatorClass is null; keying a stream by a Void-typed field.

Common situations: keyBy on a field whose type resolves to a non-comparable basic type; schema changes that make a previously-keyed field nullable/void; generic code that builds comparators for arbitrary TypeInformation without checking isKeyType().

Related errors


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