{"record":{"id":"9f4813219514b82e","repo":"apache/flink","slug":"pojo-type-expected-but-was","errorCode":null,"errorMessage":"POJO type expected but was: {}","messagePattern":"POJO type expected but was: (.+?)","errorType":"exception","errorClass":"InvalidTypesException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/common/typeinfo/Types.java","lineNumber":301,"sourceCode":"     * <p>The generic types for all fields of the POJO can be defined in a hierarchy of subclasses.\n     *\n     * <p>Java Record classes can also be used as valid POJOs (even though they don't fulfill some\n     * of the above criteria). In this case Flink will use the record canonical constructor to\n     * create the objects.\n     *\n     * <p>If Flink's type analyzer is unable to extract a valid POJO type information with type\n     * information for all fields, an {@link\n     * org.apache.flink.api.common.functions.InvalidTypesException} is thrown. Alternatively, you\n     * can use {@link Types#POJO(Class, Map)} to specify all fields manually.\n     *\n     * @param pojoClass POJO class to be analyzed by Flink\n     */\n    public static <T> TypeInformation<T> POJO(Class<T> pojoClass) {\n        final TypeInformation<T> ti = TypeExtractor.createTypeInfo(pojoClass);\n        if (ti instanceof PojoTypeInfo) {\n            return ti;\n        }\n        throw new InvalidTypesException(\"POJO type expected but was: \" + ti);\n    }\n\n    /**\n     * Returns type information for a POJO (Plain Old Java Object) and allows to specify all fields\n     * manually.\n     *\n     * <p>A type is considered a FLink POJO type, if it fulfills the conditions below.\n     *\n     * <ul>\n     *   <li>It is a public class, and standalone (not a non-static inner class)\n     *   <li>It has a public no-argument constructor.\n     *   <li>All non-static, non-transient fields in the class (and all superclasses) are either\n     *       public (and non-final) or have a public getter and a setter method that follows the\n     *       Java beans naming conventions for getters and setters.\n     *   <li>It is a fixed-length, null-aware composite type with non-deterministic field order.\n     *       Every field can be null independent of the field's type.\n     * </ul>\n     *","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/common/typeinfo/Types.java#L283-L319","documentation":"Thrown by Types.POJO(Class) when Flink's TypeExtractor cannot extract a valid PojoTypeInfo from the given class. The analyzer fell back to a different TypeInformation (typically GenericTypeInfo/Kryo, or TupleTypeInfo, or a primitive type) because the class violated one or more POJO requirements: not public, a non-static inner class, missing a public no-arg constructor, or fields that are neither public-and-non-final nor properly getter/setter accessible.","triggerScenarios":"Calling Types.POJO(MyClass.class) where MyClass is a non-static inner class, has no public no-arg constructor, has private fields without JavaBeans getters/setters, has final fields, or has interface/generic fields the analyzer cannot resolve. Also triggered when the class is an enum, an interface, an array type, or is itself recognized as a Tuple subclass.","commonSituations":"Defining a DataStream or Table sink POJO with private fields and no getters/setters. Using a non-static inner class as a POJO. Annotating a class with Lombok @Builder without @NoArgsConstructor. Having a field whose type is Object or an unbounded generic. Migrating from Tuple to POJO and forgetting to add accessors.","solutions":["Ensure the class is public, top-level or static-inner, and has a public no-argument constructor.","Make every non-static non-transient field either public (and non-final) or provide public JavaBeans-compliant getter and setter for it.","If a field's type cannot be auto-resolved, use Types.POJO(Class, Map) to specify all fields and their TypeInformation manually.","If the class genuinely cannot be made a POJO, use Types.GENERIC(Class) to accept Kryo serialization, or switch to a Tuple/Row/POJO-registered alternative.","Run TypeExtractor.createTypeInfo(MyClass.class) in a unit test and inspect the returned TypeInformation to see what Flink inferred instead."],"exampleFix":"// before — private fields, no accessors\npublic class Event {\n    private String id;\n    private long ts;\n}\nTypeInformation<Event> ti = Types.POJO(Event.class); // throws\n\n// after — public fields or getters/setters + no-arg constructor\npublic class Event {\n    public String id;\n    public long ts;\n    public Event() {}\n}\nTypeInformation<Event> ti = Types.POJO(Event.class); // ok\n\n// alternative — specify fields manually\nMap<String, TypeInformation<?>> fields = new HashMap<>();\nfields.put(\"id\", Types.STRING);\nfields.put(\"ts\", Types.LONG);\nTypeInformation<Event> ti = Types.POJO(Event.class, fields);","handlingStrategy":"type-guard","validationCode":"// Validate POJO requirements before calling Types.POJO\npublic static boolean isLikelyValidPojo(Class<?> clazz) {\n    int modifiers = clazz.getModifiers();\n    if (!Modifier.isPublic(modifiers)) return false;\n    if (clazz.isMemberClass() && !Modifier.isStatic(modifiers)) return false;\n    try {\n        clazz.getConstructor(); // public no-arg\n    } catch (NoSuchMethodException e) {\n        return false;\n    }\n    return true;\n}\n\nif (isLikelyValidPojo(MyType.class)) {\n    TypeInformation<MyType> ti = Types.POJO(MyType.class);\n}","typeGuard":"// Type guard that inspects extracted type info before assuming POJO\nTypeInformation<MyType> extracted = TypeExtractor.createTypeInfo(MyType.class);\nif (extracted instanceof PojoTypeInfo) {\n    PojoTypeInfo<MyType> pojo = (PojoTypeInfo<MyType>) extracted;\n    // safe to use as POJO\n} else {\n    // fall back to Types.GENERIC or Types.POJO(Class, Map)\n}","tryCatchPattern":"try {\n    TypeInformation<MyType> ti = Types.POJO(MyType.class);\n} catch (InvalidTypesException e) {\n    // log and fall back to manual POJO spec or GENERIC\n    Map<String, TypeInformation<?>> fields = Map.of(\n        \"id\", Types.STRING,\n        \"ts\", Types.LONG\n    );\n    ti = Types.POJO(MyType.class, fields);\n}","preventionTips":["Write a unit test that calls Types.POJO on every POJO you register and asserts it returns PojoTypeInfo.","Follow the POJO contract: public class, public no-arg constructor, public non-final fields or JavaBeans getters/setters.","Use Lombok @NoArgsConstructor + @Getter/@Setter or @Data to satisfy accessor requirements.","Avoid Object/abstract/unbounded-generic fields on POJOs; specify concrete types."],"tags":["type-system","pojo","type-extraction","invalid-types-exception"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}