apache/flink · error · InvalidTypesException
Tuple type expected.
Error message
Tuple type expected.
What it means
Thrown during input type validation when the TypeInformation is TupleTypeInfo but the reflected Type is not a Tuple subclass. The check `Tuple.class.isAssignableFrom(typeToClass(type))` fails, meaning the function expects a Tuple input but the actual type in the function signature or the stream is not a Tuple at all.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java:1534
TypeInformation<?> actual;
// check if SQL time type at all
if (!(type instanceof Class<?>)
|| (actual = SqlTimeTypeInfo.getInfoFor((Class<?>) type)) == null) {
throw new InvalidTypesException("SQL time type expected.");
}
// check if correct SQL time type
if (!typeInfo.equals(actual)) {
throw new InvalidTypesException(
"SQL time type '" + typeInfo + "' expected but was '" + actual + "'.");
}
}
// check for Java Tuples
else if (typeInfo instanceof TupleTypeInfo) {
// check if tuple at all
if (!(isClassType(type) && Tuple.class.isAssignableFrom(typeToClass(type)))) {
throw new InvalidTypesException("Tuple type expected.");
}
// do not allow usage of Tuple as type
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;
}View on GitHub (pinned to 2f3c205e92)
Solutions
- Align the function's input type to be a Tuple subclass matching the stream's TupleTypeInfo
- If the stream carries a POJO, convert it to a Tuple before the function, or change the function to accept the POJO
- Use projection or mapping to transform the element type before the function
Example fix
// before — function expects POJO, stream carries Tuple
DataStream<Tuple2<String, Integer>> stream = ...;
stream.map(new MapFunction<MyEvent, X>() { ... });
// after — align to Tuple
stream.map(new MapFunction<Tuple2<String, Integer>, X>() {
public X map(Tuple2<String, Integer> t) { ... }
}); Defensive patterns
Strategy: validation
Validate before calling
// Verify the function's input type is a Tuple subclass when stream carries TupleTypeInfo
if (stream.getType() instanceof TupleTypeInfo) {
Class<?> funcInputClass = myFunctionInputClass; // from reflection
if (!Tuple.class.isAssignableFrom(funcInputClass)) {
throw new IllegalStateException(
"Stream carries Tuple but function input "
+ funcInputClass.getName() + " is not a Tuple subclass");
}
} Prevention
- Match function input Tuple types to the stream's TupleTypeInfo
- Use explicit projections with `.project()` to ensure Tuple structure alignment
- Convert between POJOs and Tuples explicitly with map operators
- Use keyBy() return types consistently with the consuming function's input
When it happens
Trigger: A function or operator expects a Tuple as input (e.g. `MapFunction<Tuple2<String,Integer>, X>`) but the reflected type from the class hierarchy is a POJO, a basic type, or a non-Tuple class. Or the DataStream's TypeInformation is TupleTypeInfo but the function's declared input type is not a Tuple.
Common situations: Using keyBy() which produces Tuple-typed keys, then applying a function that expects a non-Tuple type. Connecting a Tuple-emitting source to a function designed for POJOs. Mismatch between projected fields (as Tuple) and the consuming function's type.
Related errors
- Concrete subclass of Tuple expected.
- Tuple arity '{}' expected but was '{}'.
- Basic type expected.
- Basic type '{}' expected but was '{}'.
- SQL time type expected.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/a0fc5c50d254aad8.
Report an issue: GitHub.