{"record":{"id":"f1e092b579c22a25","repo":"apache/druid","slug":"expected-instance-of-s-got-s","errorCode":null,"errorMessage":"Expected instance of %s, got %s","messagePattern":"Expected instance of (.+?), got (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaDataSourceMetadata.java","lineNumber":108,"sourceCode":"      return this;\n    }\n  }\n\n  @Override\n  protected SeekableStreamDataSourceMetadata<KafkaTopicPartition, Long> createConcreteDataSourceMetaData(\n      SeekableStreamSequenceNumbers<KafkaTopicPartition, Long> seekableStreamSequenceNumbers\n  )\n  {\n    return new KafkaDataSourceMetadata(seekableStreamSequenceNumbers, getBoundedStreamConfig());\n  }\n\n  @Override\n  // This method is to compare KafkaDataSourceMetadata.\n  // It compares this and other SeekableStreamSequenceNumbers using naturalOrder comparator.\n  public int compareTo(KafkaDataSourceMetadata other)\n  {\n    if (!getClass().equals(other.getClass())) {\n      throw new IAE(\n          \"Expected instance of %s, got %s\",\n          this.getClass().getName(),\n          other.getClass().getName()\n      );\n    }\n    return getSeekableStreamSequenceNumbers().compareTo(other.getSeekableStreamSequenceNumbers(), Comparator.naturalOrder());\n  }\n\n  @Override\n  public boolean matches(DataSourceMetadata other)\n  {\n    if (!getClass().equals(other.getClass())) {\n      return false;\n    }\n    KafkaDataSourceMetadata thisPlusOther = (KafkaDataSourceMetadata) plus(other);\n    if (thisPlusOther.equals(other.plus(this))) {\n      return true;\n    }","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/kafka-indexing-service/src/main/java/org/apache/druid/indexing/kafka/KafkaDataSourceMetadata.java#L90-L126","documentation":"KafkaDataSourceMetadata.compareTo() only knows how to compare against another instance of the same concrete class; comparing against a different DataSourceMetadata subclass throws IAE('Expected instance of %s, got %s').","triggerScenarios":"Calling compareTo (e.g. during metadata segment sorting/merging in coordinator/handoff paths) with a KafkaDataSourceMetadata of a different concrete class or another datasource metadata type.","commonSituations":"Mixing datasource metadata types (e.g. legacy vs current classes after a version upgrade) for the same datasource in metadata storage.","solutions":["Ensure only one concrete KafkaDataSourceMetadata class is used for a datasource; migrate/merge stored metadata","Do not compare metadata objects of different datasources/types","If comparing arbitrary metadata, use equals or type-check first"],"exampleFix":"// before\nmetadataA.compareTo(metadataB); // may be a different subclass\n// after\nif (metadataA.getClass().equals(metadataB.getClass())) { metadataA.compareTo(metadataB); }","handlingStrategy":"type-guard","validationCode":"if (a.getClass() != b.getClass()) throw new IllegalArgumentException(\"cannot compare different metadata types\");","typeGuard":"boolean comparable(org.apache.druid.metadata.DateWeightedVector... ) {} // no; use: boolean isSameClass(DataSourceMetadata a, DataSourceMetadata b) { return a.getClass().equals(b.getClass()); }","tryCatchPattern":"try {\n  return a.compareTo(b);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Expected instance of\")) { return 0; /* or handle as incomparable */ }\n  throw e;\n}","preventionTips":["Keep one metadata class per datasource","Clean up metadata after version migrations","Type-check before comparing DataSourceMetadata objects"],"tags":["comparison","metadata","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}