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 registerTypeWithTypeInfoFactory (as InvalidTypesException) when a TypeInfoFactory is already registered for the given type. The registeredTypeInfoFactories map allows only one factory per type; a second registration indicates a conflict. This commonly arises from duplicate config entries or from registering the same type via both the API and the serialization-config.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/common/serialization/SerializerConfigImpl.java:478

                loadClass(m.get("class"), classLoader, "Could not load TypeInfoFactory's class");
        // Register in the global static factory map of TypeExtractor for now so that it can be
        // accessed from the static methods of TypeExtractor where SerializerConfig is currently
        // not accessible
        TypeExtractor.registerFactory(t, factoryClass);
        // Register inside SerializerConfig only for testing purpose for now
        registerTypeWithTypeInfoFactory(t, factoryClass);
    }

    private void registerTypeWithTypeInfoFactory(
            Class<?> 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);
    }

    @Override
    public SerializerConfigImpl copy() {
        final SerializerConfigImpl newSerializerConfig = new SerializerConfigImpl();
        newSerializerConfig.configure(configuration, this.getClass().getClassLoader());

        getRegisteredTypesWithKryoSerializers()
                .forEach(
                        (c, s) ->
                                newSerializerConfig.registerTypeWithKryoSerializer(
                                        c, s.getSerializer()));
        getRegisteredTypesWithKryoSerializerClasses()
                .forEach(newSerializerConfig::registerTypeWithKryoSerializer);
        getDefaultKryoSerializers()

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Remove the duplicate typeinfo registration so each type has exactly one factory.
  2. If registering programmatically, check registeredTypeInfoFactories or wrap registration to skip if already present.
  3. Audit the serialization-config for repeated typeinfo entries targeting the same class.

Example fix

# before (duplicate typeinfo for same class)
pipeline.serialization-config: "class:com.example.MyType{type:typeinfo,class:com.example.FooFactory};class:com.example.MyType{type:typeinfo,class:com.example.BarFactory}"

# after (single factory)
pipeline.serialization-config: "class:com.example.MyType{type:typeinfo,class:com.example.FooFactory}"
Defensive patterns

Strategy: validation

Validate before calling

// Check for existing registration before adding
// (registerTypeWithTypeInfoFactory does not expose a contains check externally,
//  so deduplicate at the config/source level)
Set<Class<?>> seen = new HashSet<>();
for (String entry : configEntries) {
    Class<?> type = parseType(entry);
    if (!seen.add(type)) {
        throw new IllegalStateException("Duplicate TypeInfoFactory for: " + type);
    }
}

Try / catch

try {
    config.registerTypeWithTypeInfoFactory(type, factory);
} catch (InvalidTypesException e) {
    if (e.getMessage().contains("already registered")) {
        // skip or replace existing registration
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling registerTypeWithTypeInfoFactory twice for the same type Class, or having two serialization-config entries with type 'typeinfo' for the same class. The containsKey check fires before the put.

Common situations: Duplicate typeinfo entries in the serialization-config string; programmatically registering a factory that is also configured via pipeline options; library code that registers a factory conflicting with user config.

Related errors


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