apache/pulsar · error · IncompatibleSchemaException
Incompatible schema: exists schema type %s, new schema type
Error message
Incompatible schema: exists schema type %s, new schema type %s
What it means
SchemaRegistryServiceImpl.checkCompatible rejects a schema update whose SchemaType differs from the currently stored schema (SchemaRegistryServiceImpl.java:348). Type compatibility (e.g. existing JSON vs new Avro) is never allowed, independent of strategy; the message reports both the existing and the new type. After the type check passes, schema hash equality is what permits a same-type update.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/SchemaRegistryServiceImpl.java:348
schemaStorage.close();
this.stats.close();
}
private SchemaInfo deleted(String schemaId, String user) {
return new SchemaInfo()
.setSchemaId(schemaId)
.setType(SchemaInfo.SchemaType.NONE)
.setSchema(new byte[0])
.setUser(user)
.setDeleted(true)
.setTimestamp(clock.millis());
}
private void checkCompatible(SchemaAndMetadata existingSchema, SchemaData newSchema,
SchemaCompatibilityStrategy strategy) throws IncompatibleSchemaException {
SchemaData existingSchemaData = existingSchema.schema;
if (newSchema.getType() != existingSchemaData.getType()) {
throw new IncompatibleSchemaException(String.format("Incompatible schema: "
+ "exists schema type %s, new schema type %s",
existingSchemaData.getType(), newSchema.getType()));
}
SchemaHash existingHash = SchemaHash.of(existingSchemaData);
SchemaHash newHash = SchemaHash.of(newSchema);
if (!newHash.equals(existingHash)) {
compatibilityChecks.getOrDefault(newSchema.getType(), SchemaCompatibilityCheck.DEFAULT)
.checkCompatible(existingSchemaData, newSchema, strategy);
}
}
public CompletableFuture<Long> findSchemaVersion(String schemaId, SchemaData schemaData) {
return trimDeletedSchemaAndGetList(schemaId)
.thenCompose(schemaAndMetadataList -> {
SchemaHash newHash = SchemaHash.of(schemaData);
for (SchemaAndMetadata schemaAndMetadata : schemaAndMetadataList) {
if (newHash.equals(SchemaHash.of(schemaAndMetadata.schema))) {
return completedFuture(((LongSchemaVersion) schemaStorageView on GitHub (pinned to 820761864e)
Solutions
- Use the same SchemaType as the existing schema for the topic (check with pulsar-admin schemas get <topic>).
- If the type change is intentional, delete the schema first: pulsar-admin schemas delete <topic>, then register the new type (existing data may not deserialize under the new schema).
- Create the new type under a new topic/namespace instead of evolving the old one.
- Fix client code that builds SchemaInfo with the wrong type (e.g. Schema.AVRO(...) vs Schema.JSON(...)) so it matches the topic.
Example fix
// before
Producer<User> producer = client.newProducer(Schema.AVRO(User.class)) // topic schema is JSON
.topic("persistent://tenant/ns/orders").create();
// after
Producer<User> producer = client.newProducer(Schema.JSON(User.class))
.topic("persistent://tenant/ns/orders").create(); Defensive patterns
Strategy: validation
Validate before calling
SchemaInfo current = admin.schemas().getSchemaInfo(topic);
SchemaType existing = current.getType();
SchemaType incoming = myInfo.getType();
if (existing != incoming) {
throw new IllegalStateException("Topic schema is " + existing + " but new schema is " + incoming);
} Type guard
boolean sameType(SchemaInfo existing, SchemaInfo incoming) {
return existing != null && existing.getType() == incoming.getType();
} Try / catch
try {
admin.schemas().createSchema(topic, newInfo);
} catch (PulsarAdminException e) {
if (e.getMessage() != null && e.getMessage().contains("Incompatible schema: exists schema type")) {
// existing type differs: decide to delete schema or use matching type
} else {
throw e;
}
} Prevention
- Always fetch the current schema (pulsar-admin schemas get) before registering a new one.
- Keep one serialization framework per topic; document the topic's schema type for all producer teams.
- For intentional type migration, plan a new topic instead of deleting the schema on the live topic.
When it happens
Trigger: Uploading a schema via admin.schemas().createSchema/uploadSchema (or producer schema registration) to a topic that already has a schema of a different type — e.g. topic registered as JSON, now pushing Avro; or an AVRO vs PROTOBUF_NATIVE switch; also hits when a consumer's auto-detected schema resolves to a different type than the stored one.
Common situations: Migrating serialization format (JSON -> Avro) without deleting/resetting the schema; two teams independently picked different schema frameworks for the same topic; client library defaulting to AVRO while the topic was created with JSON; re-registering after a type was changed in the data model.
Related errors
- RestException(conversionError)
- RuntimeException(conversionError)
- Incompatible schema: expected Avro schema format for SchemaT
- Incompatible schema: existing schema is not in valid Avro fo
- Schema compatibility strategy is ALWAYS_INCOMPATIBLE
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/72eb92ca2a24e20c.
Report an issue: GitHub.