apache/flink · error · InvalidTypesException

Parameterized Tuple type expected.

Error message

Parameterized Tuple type expected.

What it means

Thrown during input type validation for Tuple types when the immediate child of Tuple in the class hierarchy is a raw Class rather than a ParameterizedType. After walking up the hierarchy to the immediate Tuple subclass, if that subclass is used without generics (e.g. `extends Tuple2` without `<String,Integer>`), the field types are erased and cannot be validated.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java:1556

                if (isClassType(type) && typeToClass(type).equals(Tuple.class)) {
                    throw new InvalidTypesException("Concrete subclass of Tuple expected.");
                }

                // go up the hierarchy until we reach immediate child of Tuple (with or without
                // generics)
                while (!(isClassType(type)
                        && typeToClass(type).getSuperclass().equals(Tuple.class))) {
                    typeHierarchy.add(type);
                    type = typeToClass(type).getGenericSuperclass();
                }

                if (type == Tuple0.class) {
                    return;
                }

                // check if immediate child of Tuple has generics
                if (type instanceof Class<?>) {
                    throw new InvalidTypesException("Parameterized Tuple type expected.");
                }

                TupleTypeInfo<?> tti = (TupleTypeInfo<?>) typeInfo;

                Type[] subTypes = ((ParameterizedType) type).getActualTypeArguments();

                if (subTypes.length != tti.getArity()) {
                    throw new InvalidTypesException(
                            "Tuple arity '"
                                    + tti.getArity()
                                    + "' expected but was '"
                                    + subTypes.length
                                    + "'.");
                }

                for (int i = 0; i < subTypes.length; i++) {
                    validateInfo(new ArrayList<>(typeHierarchy), subTypes[i], tti.getTypeAt(i));
                }

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Parameterize the Tuple subclass: `class MyKey extends Tuple2<String, Integer> {}`
  2. Use a concrete Tuple directly in the function signature instead of a raw subclass
  3. Provide TypeInformation explicitly via `.returns(...)` to bypass validation

Example fix

// before — raw Tuple subclass
class MyKey extends Tuple2 {}
public class MyFunc extends MapFunction<MyKey, String> { ... }

// after — parameterized
class MyKey extends Tuple2<String, Integer> {}
public class MyFunc extends MapFunction<MyKey, String> { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Verify Tuple subclass is parameterized
Class<?> tupleSubclass = MyKey.class;
Type superType = tupleSubclass.getGenericSuperclass();
// Walk up to immediate Tuple child
while (superType instanceof Class<?> && !((Class<?>) superType).equals(Tuple.class)) {
    superType = ((Class<?>) superType).getGenericSuperclass();
}
if (superType instanceof Class<?> && tupleSubclass != Tuple0.class) {
    throw new IllegalStateException(
        "Tuple subclass " + tupleSubclass.getName()
        + " is not parameterized. Use: extends Tuple2<Type1, Type2>");
}

Prevention

When it happens

Trigger: A function input type is a Tuple subclass that extends a Tuple without specifying type parameters — e.g. `class MyKey extends Tuple2 {}`. The validation code walks up to the immediate Tuple child and finds it is a Class (not ParameterizedType), meaning the field types have been erased.

Common situations: Custom Tuple subclasses defined without type parameters. Legacy Java code that uses raw Tuple subclasses. Tuple subclasses from libraries that suppress generics.

Related errors


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