apache/flink · error · InvalidTypesException

A TypeInfoFactory for type '{}' is already registered.

Error message

A TypeInfoFactory for type '{}' is already registered.

What it means

Thrown by TypeExtractor.registerFactory when a TypeInfoFactory has already been registered for the given Type. The registration is stored in a static ConcurrentHashMap (registeredTypeInfoFactories) keyed by the Type. Attempting to register a second factory for the same Type is rejected to prevent ambiguous type information resolution.

Source

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

     * Registers a type information factory globally for a certain type. Every following type
     * extraction operation will use the provided factory for this type. The factory will have the
     * highest precedence for this type. In a hierarchy of types the registered factory has higher
     * precedence than annotations at the same level but lower precedence than factories defined
     * down the hierarchy.
     *
     * @param t type for which a new factory is registered
     * @param factory type information factory that will produce {@link TypeInformation}
     */
    @Internal
    public static void registerFactory(Type t, Class<? extends TypeInfoFactory<?>> factory) {
        Preconditions.checkNotNull(t, "Type parameter must not be null.");
        Preconditions.checkNotNull(factory, "Factory parameter must not be null.");

        if (!TypeInfoFactory.class.isAssignableFrom(factory)) {
            throw new IllegalArgumentException("Class is not a TypeInfoFactory.");
        }
        if (registeredTypeInfoFactories.containsKey(t)) {
            throw new InvalidTypesException(
                    "A TypeInfoFactory for type '" + t + "' is already registered.");
        }
        registeredTypeInfoFactories.put(t, factory);
    }

    // --------------------------------------------------------------------------------------------
    //  Function specific methods
    // --------------------------------------------------------------------------------------------

    @PublicEvolving
    public static <IN, OUT> TypeInformation<OUT> getMapReturnTypes(
            MapFunction<IN, OUT> mapInterface, TypeInformation<IN> inType) {
        return getMapReturnTypes(mapInterface, inType, null, false);
    }

    @PublicEvolving
    public static <IN, OUT> TypeInformation<OUT> getMapReturnTypes(
            MapFunction<IN, OUT> mapInterface,

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Consolidate to a single TypeInfoFactory registration per type.
  2. If different factories are needed for the same type in different contexts, use a single factory that dispatches based on runtime conditions.
  3. For tests, run each test in a separate JVM or avoid re-registering the same type.
  4. Check registeredTypeInfoFactories before registering if you suspect a prior registration.

Example fix

// before
TypeExtractor.registerFactory(MyType.class, FactoryA.class);
// ... later in another module ...
TypeExtractor.registerFactory(MyType.class, FactoryB.class); // throws
// after
// Register only once, in a shared location:
TypeExtractor.registerFactory(MyType.class, FactoryA.class);
Defensive patterns

Strategy: validation

Validate before calling

// Check before registering — there is no public getter, so track in your own code
if (alreadyRegisteredTypes.contains(t)) {
    throw new IllegalStateException("Factory already registered for " + t);
}
TypeExtractor.registerFactory(t, factory);
alreadyRegisteredTypes.add(t);

Try / catch

try {
    TypeExtractor.registerFactory(type, factory);
} catch (InvalidTypesException e) {
    if (e.getMessage().contains("already registered")) {
        // already registered by another module; acceptable
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling TypeExtractor.registerFactory(MyType.class, FactoryA.class) and later TypeExtractor.registerFactory(MyType.class, FactoryB.class). The static map persists for the JVM lifetime, so two separate calls in the same process for the same type trigger this.

Common situations: Multiple modules or libraries each register a TypeInfoFactory for the same type. Static initialization blocks in different classes both registering factories for a shared type. Unit tests that register factories without cleanup and then run in the same JVM.

Related errors


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