apache/flink · error · InvalidTypesException

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

Error message

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

What it means

Thrown by TypeExtractor.validateInfo when a PojoTypeInfo is expected but the reflected type's class does not exactly match the PojoTypeInfo's type class. This means the type extractor expected a specific POJO class but found a different class through reflection.

Source

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

                if (!(type instanceof Class<?> && Value.class.isAssignableFrom((Class<?>) type))) {
                    throw new InvalidTypesException("Value type expected.");
                }

                TypeInformation<?> actual;
                // check value type contents
                if (!typeInfo.equals(
                        actual = ValueTypeInfo.getValueTypeInfo((Class<? extends Value>) type))) {
                    throw new InvalidTypesException(
                            "Value type '" + typeInfo + "' expected but was '" + actual + "'.");
                }
            }
            // check for POJO
            else if (typeInfo instanceof PojoTypeInfo) {
                Class<?> clazz = null;
                if (!(isClassType(type)
                        && ((PojoTypeInfo<?>) typeInfo).getTypeClass()
                                == (clazz = typeToClass(type)))) {
                    throw new InvalidTypesException(
                            "POJO type '"
                                    + ((PojoTypeInfo<?>) typeInfo).getTypeClass().getCanonicalName()
                                    + "' expected but was '"
                                    + clazz.getCanonicalName()
                                    + "'.");
                }
            }
            // check for Enum
            else if (typeInfo instanceof EnumTypeInfo) {
                if (!(type instanceof Class<?> && Enum.class.isAssignableFrom((Class<?>) type))) {
                    throw new InvalidTypesException("Enum type expected.");
                }
                // check enum type contents
                if (!(typeInfo.getTypeClass() == type)) {
                    throw new InvalidTypesException(
                            "Enum type '"
                                    + typeInfo.getTypeClass().getCanonicalName()
                                    + "' expected but was '"

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure the actual runtime class matches the declared PojoTypeInfo class exactly.
  2. Provide explicit TypeInformation via .returns(TypeInformation.of(MyPojo.class)) matching the actual class.
  3. If using inheritance, register the correct concrete POJO class, not the parent.
  4. Check for stale type hints after class refactoring.

Example fix

// before
.returns(TypeInformation.of(ParentPojo.class)) // but actual is ChildPojo
// after
.returns(TypeInformation.of(ChildPojo.class))
Defensive patterns

Strategy: validation

Validate before calling

// Check POJO class match before extraction
Class<?> expectedClass = ((PojoTypeInfo<?>) typeInfo).getTypeClass();
Class<?> actualClass = typeToClass(type);
if (expectedClass != actualClass) {
    // provide correct PojoTypeInfo
    ti = TypeInformation.of(actualClass);
}

Type guard

static boolean pojoClassesMatch(TypeInformation<?> expected, Type actual) {
    return expected instanceof PojoTypeInfo<?>
        && isClassType(actual)
        && ((PojoTypeInfo<?>) expected).getTypeClass() == typeToClass(actual);
}

Try / catch

try {
    validateInfo(hierarchy, type, pojoTypeInfo);
} catch (InvalidTypesException e) {
    ti = TypeInformation.of(typeToClass(type));
}

Prevention

When it happens

Trigger: Called during validateInfo when TypeInformation is a PojoTypeInfo and either the type is not a ClassType, or typeToClass(type) differs from the PojoInfo's getTypeClass(). Occurs when the declared POJO type class does not match the actual generic type argument resolved by reflection.

Common situations: Using a subclass of a POJO where the parent was expected, or vice versa. Generic functions where the POJO class is parameterized and erasure resolves to a different class. Refactoring POJO classes (renaming/moving) without updating type hints or serialized state.

Related errors


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