apache/druid · error · ISE
Incompatible strategy for type
Error message
Incompatible strategy for type[%s] already exists. Expected [%s], found [%s].
What it means
TypeStrategies.registerComplex() registers the serialization strategy for a complex type. If a strategy for the same complex type name is already registered with a different implementation class, it throws IllegalStateException to prevent silently swapping serializers, which would corrupt deserialization of existing segments.
Solutions
- Ensure only one implementation registers the strategy for that complex type name
- Remove duplicate/shaded jars providing conflicting TypeStrategy classes
- Align extension versions across the cluster so the same strategy class is used
Example fix
// before
ComplexMetrics.registerSerde("myType", new MyCustomSerde()); // conflicts with existing
// after
ComplexMetrics.registerSerde("myType", MyStandardSerde.class == alreadyRegistered.getClass()
? alreadyRegistered : new MyStandardSerde()); Defensive patterns
Strategy: validation
Validate before calling
TypeStrategy existing = ComplexMetrics.getSerdeForType(typeName); if (existing != null && !existing.getClass().equals(myStrategy.getClass())) { throw new ConflictingRegistration(typeName); } Type guard
boolean isCompatible(TypeStrategy existing, TypeStrategy candidate) { return existing == null || existing.getClass().getName().equals(candidate.getClass().getName()); } Try / catch
try { TypeStrategies.registerComplex(typeName, strategy); } catch (ISE e) { log.warn("Strategy already registered for %s, reusing existing", typeName); } Prevention
- Register complex types in exactly one place per JVM
- Avoid shaded/duplicate jars of extension classes
- Keep extension versions aligned across the cluster
When it happens
Trigger: Registering a complex type (e.g. via ComplexMetrics.registerSerDe or extension initialization) where another extension/classloader already registered a different TypeStrategy class for the same type name.
Common situations: Two extensions registering the same complex type name with different serializer implementations; duplicate classes on the classpath (shaded jars); Druid clusters with mixed extension versions.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Couldn't deserialize authenticator userMap!
- Couldn't deserialize authorizer groupMappingMap!
- Couldn't deserialize authorizer userMap!
- Couldn't serialize authenticator userMap!
- Couldn't serialize authorizer userMap!
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/1ee3eccd5b42f01e.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/segment/column/TypeStrategies.java:89
/**
* hmm... this might look familiar... (see ComplexMetrics)
* <p>
* Register a complex type name -> {@link TypeStrategy} mapping.
* <p>
* If the specified type name is already used and the supplied {@link TypeStrategy} is not of the
* same type as the existing value in the map for said key, an {@link ISE} is thrown.
*
* @param strategy The {@link TypeStrategy} object to be associated with the 'type' in the map.
*/
public static void registerComplex(String typeName, TypeStrategy<?> strategy)
{
Preconditions.checkNotNull(typeName);
COMPLEX_STRATEGIES.compute(typeName, (key, value) -> {
if (value == null) {
return strategy;
} else {
if (!value.getClass().getName().equals(strategy.getClass().getName())) {
throw new ISE(
"Incompatible strategy for type[%s] already exists. Expected [%s], found [%s].",
key,
strategy.getClass().getName(),
value.getClass().getName()
);
} else {
return value;
}
}
});
}
/**
* Clear and set the 'null' byte of a nullable value to {@link TypeStrategies#IS_NULL_BYTE} to a {@link ByteBuffer} at
* the supplied position. This method does not change the buffer position, limit, or mark, because it does not expect
* to own the buffer given to it (i.e. buffer aggs)
* <p>
* Nullable types are stored with a leading byte to indicate if the value is null, followed by the value bytesView on GitHub (pinned to 9b90983fd2)