{"record":{"id":"f8eab6934ff26634","repo":"apache/pulsar","slug":"incompatible-schema-from-s-to-s","errorCode":null,"errorMessage":"Incompatible Schema from %s + to %s","messagePattern":"Incompatible Schema from (.+?) \\+ to (.+?)","errorType":"exception","errorClass":"IncompatibleSchemaException","httpStatus":null,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/JsonSchemaCompatibilityCheck.java","lineNumber":99,"sourceCode":"            // When legacy format is disabled, the existing schema must be valid Avro.\n            // If it's not, this is a defense-in-depth rejection (PIP-464).\n            throw new IncompatibleSchemaException(\n                    \"Incompatible schema: existing schema is not in valid Avro format for SchemaType.JSON\");\n        } else {\n            // broker has schema format with unknown format\n            // maybe corrupted?\n            // return true to overwrite\n        }\n    }\n\n    private static final ObjectReader JSON_SCHEMA_READER =\n            ObjectMapperFactory.getMapper().reader().forType(JsonSchema.class);\n    private void isCompatibleJsonSchema(SchemaData from, SchemaData to) throws IncompatibleSchemaException {\n        try {\n            JsonSchema fromSchema = JSON_SCHEMA_READER.readValue(from.getData());\n            JsonSchema toSchema = JSON_SCHEMA_READER.readValue(to.getData());\n            if (!fromSchema.getId().equals(toSchema.getId())) {\n                throw new IncompatibleSchemaException(String.format(\"Incompatible Schema from %s + to %s\",\n                        new String(from.getData(), UTF_8), new String(to.getData(), UTF_8)));\n            }\n        } catch (IOException e) {\n            throw new IncompatibleSchemaException(e);\n        }\n    }\n\n    private boolean isAvroSchema(SchemaData schemaData) {\n        try {\n\n            Schema.Parser fromParser = new Schema.Parser(StructSchemaDataValidator.COMPATIBLE_NAME_VALIDATOR);\n            fromParser.setValidateDefaults(false);\n            Schema fromSchema = fromParser.parse(new String(schemaData.getData(), UTF_8));\n            return true;\n        } catch (Exception e) {\n            return false;\n        }\n    }","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/schema/JsonSchemaCompatibilityCheck.java#L81-L117","documentation":"When both the existing and the new JSON schemas are in the legacy Jackson format, compatibility is decided by comparing their JSON 'id' fields; if the ids differ the schemas are considered incompatible and the full schema payloads are included in the error message.","triggerScenarios":"checkCompatible -> isCompatibleJsonSchema with both schemas parseable as legacy Jackson JsonSchema documents whose getId() values are not equal.","commonSituations":"Two different POJOs (or the same POJO renamed/refactored) serialized with legacy clients uploading schemas to the same topic; teams reusing a topic across unrelated JSON message types.","solutions":["Ensure all producers for the topic use POJOs that yield the same JsonSchema id (same schema definition).","Re-register the topic schema with the new definition in Avro format (delete schema first if needed).","Migrate off the legacy Jackson format to Avro-format JSON schemas so normal Avro compatibility checks apply."],"exampleFix":"// before: different ids\nJsonSchema a = new JsonSchema(\"com.x.A\", \"schema1\"); // id: schema1\nJsonSchema b = new JsonSchema(\"com.x.B\", \"schema2\"); // id: schema2\n// after: aligned id\nadmin.schemas().createSchema(topic, Schema.JSON(Pojo.class).getSchemaInfo());","handlingStrategy":"try-catch","validationCode":"// pre-compare legacy JsonSchema ids client-side\nJsonSchema from = MAPPER.readValue(existingBytes, JsonSchema.class);\nJsonSchema to = MAPPER.readValue(newBytes, JsonSchema.class);\nif (!from.getId().equals(to.getId())) throw new IllegalArgumentException(\"JsonSchema id mismatch\");","typeGuard":"boolean sameJsonSchemaId(JsonSchema a, JsonSchema b) {\n    return a != null && b != null && a.getId() != null && a.getId().equals(b.getId());\n}","tryCatchPattern":"try {\n    admin.schemas().createSchema(topic, schemaInfo);\n} catch (PulsarAdminException e) {\n    if (e.getMessage().startsWith(\"Incompatible Schema from\")) {\n        // ids differ: register new schema version or use a new topic\n    }\n}","preventionTips":["Use one POJO per topic so legacy JsonSchema ids stay consistent.","Avoid multi-team topic sharing with different JSON message types.","Migrate off legacy Jackson format to Avro-format JSON schemas.","Compare ids in CI when schema definitions change."],"tags":["pulsar","json-schema","legacy-format"],"backgroundTag":"json-schema-id-mismatch","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}