{"record":{"id":"74b805ada30efffa","repo":"apache/flink","slug":"typeinformation-cannot-be-filled-in-for-the-type-a","errorCode":null,"errorMessage":"TypeInformation cannot be filled in for the type after it has been used. Please make sure that the type info hints are the first call after the transformation function, before any access to types or semantic properties, etc.","messagePattern":"TypeInformation cannot be filled in for the type after it has been used\\. Please make sure that the type info hints are the first call after the transformation function, before any access to types or semantic properties, etc\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/dag/Transformation.java","lineNumber":563,"sourceCode":"     */\n    @Nullable\n    public String getCoLocationGroupKey() {\n        return coLocationGroupKey;\n    }\n\n    /**\n     * Tries to fill in the type information. Type information can be filled in later when the\n     * program uses a type hint. This method checks whether the type information has ever been\n     * accessed before and does not allow modifications if the type was accessed already. This\n     * ensures consistency by making sure different parts of the operation do not assume different\n     * type information.\n     *\n     * @param outputType The type information to fill in.\n     * @throws IllegalStateException Thrown, if the type information has been accessed before.\n     */\n    public void setOutputType(TypeInformation<T> outputType) {\n        if (typeUsed) {\n            throw new IllegalStateException(\n                    \"TypeInformation cannot be filled in for the type after it has been used. \"\n                            + \"Please make sure that the type info hints are the first call after\"\n                            + \" the transformation function, \"\n                            + \"before any access to types or semantic properties, etc.\");\n        }\n        this.outputType = outputType;\n    }\n\n    /**\n     * Returns the output type of this {@code Transformation} as a {@link TypeInformation}. Once\n     * this is used once the output type cannot be changed anymore using {@link #setOutputType}.\n     *\n     * @return The output type of this {@code Transformation}\n     */\n    public TypeInformation<T> getOutputType() {\n        if (outputType instanceof MissingTypeInfo) {\n            MissingTypeInfo typeInfo = (MissingTypeInfo) this.outputType;\n            throw new InvalidTypesException(","sourceCodeStart":545,"sourceCodeEnd":581,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/dag/Transformation.java#L545-L581","documentation":"Transformation.setOutputType fills in type information after a transformation is created (e.g., via type hints). It refuses to overwrite once the type has already been read (getOutputType was called), tracked by a one-way 'typeUsed' latch, so different parts of the job graph cannot assume different output types.","triggerScenarios":"Calling .returns(...) / setOutputType() AFTER getOutputType() (or any internal operation that reads the output type, such as keyBy, connect, or plan printing) has already been invoked on the same transformation.","commonSituations":"Chaining .returns(TypeInformation.of(...)) after type-dependent operations instead of immediately after the producing transformation; calling execute() or printing the execution plan before setting the type hint; debugging code that touches getOutputType() before the hint.","solutions":["Move the type hint (.returns(...)) to be the FIRST call right after the transformation function, before any type-dependent operation.","Remove any intermediate getOutputType()/plan-printing calls that run before the hint is set.","Have your function implement ResultTypeQueryable and return its TypeInformation directly, removing the need for a runtime hint.","Avoid reordering operators between the source transformation and the .returns() call."],"exampleFix":"// before: data.map(fn).keyBy(x -> x).returns(Types.POJO(MyClass.class))  // typeUsed already true -> throws\n// after:  data.map(fn).returns(Types.POJO(MyClass.class)).keyBy(x -> x)","handlingStrategy":"validation","validationCode":"// Ensure no type access happens before the hint; call returns() immediately after the transformation\nDataStream<MyPojo> mapped = source.map(fn).returns(TypeInformation.of(MyPojo.class));\n// Only after returns() may you call keyBy/connect/getOutputType/etc.","typeGuard":null,"tryCatchPattern":"try {\n    transform.setOutputType(t);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"cannot be filled in for the type after it has been used\")) {\n        log.error(\"Type hint must be set before any type access; move .returns() right after the transformation\");\n    }\n    throw e;\n}","preventionTips":["Always place .returns(...) immediately after the producing transformation, before keyBy/connect/union.","Avoid calling getOutputType() or printing the execution plan before setting type hints.","Prefer implementing ResultTypeQueryable so no runtime hint is needed.","In code review, flag any .returns() that appears after a type-dependent operator."],"tags":["type-information","type-erasure","datastream-api"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}