{"record":{"id":"1ee3eccd5b42f01e","repo":"apache/druid","slug":"incompatible-strategy-for-type-s-already-exists","errorCode":null,"errorMessage":"Incompatible strategy for type[%s] already exists. Expected [%s], found [%s].","messagePattern":"Incompatible strategy for type\\[(.+?)\\] already exists\\. Expected \\[(.+?)\\], found \\[(.+?)\\]\\.","errorType":"exception","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/column/TypeStrategies.java","lineNumber":89,"sourceCode":"  /**\n   * hmm... this might look familiar... (see ComplexMetrics)\n   * <p>\n   * Register a complex type name -> {@link TypeStrategy} mapping.\n   * <p>\n   * If the specified type name is already used and the supplied {@link TypeStrategy} is not of the\n   * same type as the existing value in the map for said key, an {@link ISE} is thrown.\n   *\n   * @param strategy The {@link TypeStrategy} object to be associated with the 'type' in the map.\n   */\n  public static void registerComplex(String typeName, TypeStrategy<?> strategy)\n  {\n    Preconditions.checkNotNull(typeName);\n    COMPLEX_STRATEGIES.compute(typeName, (key, value) -> {\n      if (value == null) {\n        return strategy;\n      } else {\n        if (!value.getClass().getName().equals(strategy.getClass().getName())) {\n          throw new ISE(\n              \"Incompatible strategy for type[%s] already exists. Expected [%s], found [%s].\",\n              key,\n              strategy.getClass().getName(),\n              value.getClass().getName()\n          );\n        } else {\n          return value;\n        }\n      }\n    });\n  }\n\n  /**\n   * Clear and set the 'null' byte of a nullable value to {@link TypeStrategies#IS_NULL_BYTE} to a {@link ByteBuffer} at\n   * the supplied position. This method does not change the buffer position, limit, or mark, because it does not expect\n   * to own the buffer given to it (i.e. buffer aggs)\n   * <p>\n   * Nullable types are stored with a leading byte to indicate if the value is null, followed by the value bytes","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/column/TypeStrategies.java#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nComplexMetrics.registerSerde(\"myType\", new MyCustomSerde()); // conflicts with existing\n// after\nComplexMetrics.registerSerde(\"myType\", MyStandardSerde.class == alreadyRegistered.getClass()\n    ? alreadyRegistered : new MyStandardSerde());","handlingStrategy":"validation","validationCode":"TypeStrategy existing = ComplexMetrics.getSerdeForType(typeName); if (existing != null && !existing.getClass().equals(myStrategy.getClass())) { throw new ConflictingRegistration(typeName); }","typeGuard":"boolean isCompatible(TypeStrategy existing, TypeStrategy candidate) { return existing == null || existing.getClass().getName().equals(candidate.getClass().getName()); }","tryCatchPattern":"try { TypeStrategies.registerComplex(typeName, strategy); } catch (ISE e) { log.warn(\"Strategy already registered for %s, reusing existing\", typeName); }","preventionTips":["Register complex types in exactly one place per JVM","Avoid shaded/duplicate jars of extension classes","Keep extension versions aligned across the cluster"],"tags":["java","serialization","extensions","conflict"],"backgroundTag":"conflicting-config-options","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}