apache/flink · error · InvalidTypesException

Tuple type expected but was: {}

Error message

Tuple type expected but was: {}

What it means

Types.TUPLE(Class<T extends Tuple>) runs TypeExtractor.createTypeInfo(tupleSubclass) and requires the result to be a TupleTypeInfo. If the extracted TypeInformation is not a TupleTypeInfo (e.g., the class does not actually extend a Tuple0..Tuple25, or Flink classifies it as a POJO/other type), an InvalidTypesException is thrown reporting the actual extracted type. The class must extend a concrete TupleN and define all field types without adding extra fields.

Source

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

     *     public int getId() { return f0; }
     *
     *     public String getName() { return f1; }
     *   }
     * }
     *
     * Types.TUPLE(MyTuple.class)
     * </pre>
     *
     * @param tupleSubclass A subclass of {@link org.apache.flink.api.java.tuple.Tuple0} till {@link
     *     org.apache.flink.api.java.tuple.Tuple25} that defines all field types and does not add
     *     any additional fields
     */
    public static <T extends Tuple> TypeInformation<T> TUPLE(Class<T> tupleSubclass) {
        final TypeInformation<T> ti = TypeExtractor.createTypeInfo(tupleSubclass);
        if (ti instanceof TupleTypeInfo) {
            return ti;
        }
        throw new InvalidTypesException("Tuple type expected but was: " + ti);
    }

    /**
     * Returns type information for a POJO (Plain Old Java Object).
     *
     * <p>A POJO class is public and standalone (no non-static inner class). It has a public
     * no-argument constructor. All non-static, non-transient fields in the class (and all
     * superclasses) are either public (and non-final) or have a public getter and a setter method
     * that follows the Java beans naming conventions for getters and setters.
     *
     * <p>A POJO is a fixed-length and null-aware composite type. Every field can be null
     * independent of the field's type.
     *
     * <p>The generic types for all fields of the POJO can be defined in a hierarchy of subclasses.
     *
     * <p>Java Record classes can also be used as valid POJOs (even though they don't fulfill some
     * of the above criteria). In this case Flink will use the record canonical constructor to
     * create the objects.

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure the class extends a concrete TupleN (Tuple0..Tuple25) and does not add fields beyond the tuple arity; then Types.TUPLE works.
  2. If the class is a POJO, use Types.POJO(MyClass.class) instead.
  3. If you just need a generic tuple, use Types.TUPLE(TypeInformation.of(String.class), ...) (the vararg overload) rather than a subclass.

Example fix

// before
class MyData { public int id; public String name; }
Types.TUPLE(MyData.class); // throws: not a Tuple subclass

// after
Types.POJO(MyData.class);
// or define a real tuple subclass:
class MyTuple extends Tuple2<Integer, String> {}
Types.TUPLE(MyTuple.class);
Defensive patterns

Strategy: type-guard

Validate before calling

Class<?> c = tupleSubclass;
if (!Tuple.class.isAssignableFrom(c)) {
    throw new IllegalArgumentException(c + " does not extend Tuple");
}
// also verify no extra fields beyond the Tuple arity
Types.TUPLE((Class<? extends Tuple>) c);

Type guard

static boolean isTupleSubclass(Class<?> c) {
    return Tuple.class.isAssignableFrom(c);
}

Try / catch

try {
    return Types.TUPLE(tupleSubclass);
} catch (InvalidTypesException e) {
    // fall back to POJO if it is not a real tuple
    return Types.POJO(tupleSubclass);
}

Prevention

When it happens

Trigger: Calling Types.TUPLE(MyClass.class) where MyClass does not extend org.apache.flink.api.java.tuple.TupleN; a Tuple subclass that adds fields or hides generics so TypeExtractor returns a non-TupleTypeInfo (e.g., PojoTypeInfo); passing a Tuple subclass whose generic supertype is erased.

Common situations: Mistakenly passing a POJO or bean class to Types.TUPLE; a Tuple subclass that adds non-tuple fields causing POJO classification; refactoring a Tuple subclass into a plain POJO and forgetting to switch from Types.TUPLE to Types.POJO.

Related errors


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