apache/flink · error · InvalidTypesException
Tuple arity '{}' expected but was '{}'.
Error message
Tuple arity '{}' expected but was '{}'. What it means
Thrown during input type validation for Tuple types when the number of fields in the TupleTypeInfo does not match the number of actual type arguments in the reflected ParameterizedType. For example, the stream carries TupleTypeInfo for Tuple2 (arity 2) but the function's signature declares Tuple3 (arity 3), or vice versa.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/TypeExtractor.java:1564
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));
}
}
// check for primitive array
else if (typeInfo instanceof PrimitiveArrayTypeInfo) {
Type component;
// check if array at all
if (!(type instanceof Class<?>
&& ((Class<?>) type).isArray()
&& (component = ((Class<?>) type).getComponentType()) != null)View on GitHub (pinned to 2f3c205e92)
Solutions
- Match the Tuple arity in the function signature to the stream's TupleTypeInfo
- If you need a different number of fields, add a map to project/reshape the Tuple
- Use a POJO with named fields if the schema changes frequently
Example fix
// before — arity mismatch
DataStream<Tuple2<String, Integer>> stream = ...;
stream.map(new MapFunction<Tuple3<String, Integer, Long>, X>() { ... });
// after — match arity
stream.map(new MapFunction<Tuple2<String, Integer>, X>() {
public X map(Tuple2<String, Integer> value) { ... }
}); Defensive patterns
Strategy: validation
Validate before calling
// Verify Tuple arity matches between stream and function
if (stream.getType() instanceof TupleTypeInfo) {
int streamArity = ((TupleTypeInfo<?>) stream.getType()).getArity();
int funcArity = getTupleArityFromFunction(myFunction); // custom helper
if (streamArity != funcArity) {
throw new IllegalStateException(
"Tuple arity mismatch: stream has " + streamArity
+ " fields but function expects " + funcArity);
}
} Prevention
- Keep Tuple arity consistent across the pipeline
- Add explicit map operators when reshaping Tuple fields
- Use TupleTypeInfo.getArity() to verify field counts before connecting operators
- Prefer POJOs with named fields when the schema changes frequently
When it happens
Trigger: The DataStream's TypeInformation is TupleTypeInfo with a specific arity (e.g. Tuple2, arity 2) but the function's declared input Tuple subclass has a different arity (e.g. Tuple3). The check `subTypes.length != tti.getArity()` fails.
Common situations: Changing the number of fields in a Tuple key or projection without updating the consuming function. Connecting a Tuple2 stream to a function expecting Tuple3. Adding or removing a field in a transformation pipeline without propagating the change.
Related errors
- Tuple type expected.
- Concrete subclass of Tuple expected.
- 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/90c60541fcca19f9.
Report an issue: GitHub.