apache/flink · error · InvalidTypesException

Writable type '{}' expected but was '{}'.

Error message

Writable type '{}' expected but was '{}'.

What it means

Thrown by TypeExtractor.validateIfWritable when a WritableTypeInfo is expected, the type is a valid Hadoop Writable, but the expected Writable type class differs from the actual class found by reflection. This indicates a mismatch between two specific Writable types.

Source

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

            // try to load the writable type info

            Class<?> writableTypeInfoClass =
                    Class.forName(
                            HADOOP_WRITABLE_TYPEINFO_CLASS,
                            false,
                            typeInfo.getClass().getClassLoader());

            if (writableTypeInfoClass.isAssignableFrom(typeInfo.getClass())) {
                // this is actually a writable type info
                // check if the type is a writable
                if (!(type instanceof Class && isHadoopWritable((Class<?>) type))) {
                    throw new InvalidTypesException(HADOOP_WRITABLE_CLASS + " type expected.");
                }

                // check writable type contents
                Class<?> clazz = (Class<?>) type;
                if (typeInfo.getTypeClass() != clazz) {
                    throw new InvalidTypesException(
                            "Writable type '"
                                    + typeInfo.getTypeClass().getCanonicalName()
                                    + "' expected but was '"
                                    + clazz.getCanonicalName()
                                    + "'.");
                }
            }
        } catch (ClassNotFoundException e) {
            // class not present at all, so cannot be that type info
            // ignore
        }
    }
}

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Align the declared WritableTypeInfo with the actual Writable class used at runtime.
  2. Provide the correct explicit TypeInformation via .returns(WritableTypeInfo.of(ActualWritableClass.class)).
  3. Check for accidental Writable type substitution (e.g., Text vs BytesWritable).
  4. Update type hints after refactoring Writable types.

Example fix

// before
.returns(WritableTypeInfo.of(IntWritable.class)) // actual is LongWritable

// after
.returns(WritableTypeInfo.of(LongWritable.class))
Defensive patterns

Strategy: validation

Validate before calling

// Check that the expected Writable class matches the actual
Class<?> expectedWritable = typeInfo.getTypeClass();
Class<?> actualWritable = (Class<?>) type;
if (expectedWritable != actualWritable) {
    // align WritableTypeInfo with actual class
    ti = WritableTypeInfo.of(actualWritable);
}

Type guard

static boolean writableClassesMatch(TypeInformation<?> expected, Class<?> actual) {
    return expected.getTypeClass() == actual;
}

Try / catch

try {
    validateIfWritable(typeInfo, type);
} catch (InvalidTypesException e) {
    ti = WritableTypeInfo.of((Class<?>) type);
}

Prevention

When it happens

Trigger: Called during validateIfWritable after confirming the type is a Writable, but typeInfo.getTypeClass() != clazz (the actual class). Occurs when the declared WritableTypeInfo references one Writable class but the reflected type argument resolves to a different Writable class.

Common situations: Declaring a WritableTypeInfo for IntWritable but the function returns LongWritable. Generic UDFs where different Writable types appear in declared vs actual positions. Using a custom Writable subclass where the parent Writable was expected. Refactoring Writable type usages without updating type hints.

Related errors


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