apache/pulsar · error · RuntimeException
RuntimeException(conversionError)
Error message
RuntimeException(conversionError)
What it means
convertSchemaAndMetadataToGetSchemaResponse converts a stored SchemaAndMetadata into a GetSchemaResponse whose data is the schema definition as a UTF-8 String. If reading/converting the stored schema bytes throws IOException (the declared checked exception from schema.getData/serialization plumbing), it is rethrown as an unchecked RuntimeException, which propagates out of the admin GET schema handlers as an HTTP 500.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/SchemasResourceBase.java:210
protected String domain() {
return "persistent";
}
private static GetSchemaResponse convertSchemaAndMetadataToGetSchemaResponse(SchemaAndMetadata schemaAndMetadata) {
try {
String schemaData;
if (schemaAndMetadata.schema.getType() == SchemaType.KEY_VALUE) {
schemaData = DefaultImplementation.getDefaultImplementation().convertKeyValueSchemaInfoDataToString(
DefaultImplementation.getDefaultImplementation()
.decodeKeyValueSchemaInfo(schemaAndMetadata.schema.toSchemaInfo()));
} else {
schemaData = new String(schemaAndMetadata.schema.getData(), StandardCharsets.UTF_8);
}
return GetSchemaResponse.builder().version(getLongSchemaVersion(schemaAndMetadata.version))
.type(schemaAndMetadata.schema.getType()).timestamp(schemaAndMetadata.schema.getTimestamp())
.data(schemaData).properties(schemaAndMetadata.schema.getProps()).build();
} catch (IOException conversionError) {
throw new RuntimeException(conversionError);
}
}
protected GetSchemaResponse convertToSchemaResponse(SchemaAndMetadata schema) {
if (isNull(schema)) {
throw new RestException(Response.Status.NOT_FOUND.getStatusCode(), "Schema not found");
} else if (schema.schema.isDeleted()) {
throw new RestException(Response.Status.NOT_FOUND.getStatusCode(), "Schema is deleted");
}
return convertSchemaAndMetadataToGetSchemaResponse(schema);
}
protected GetAllVersionsSchemaResponse convertToAllVersionsSchemaResponse(List<SchemaAndMetadata> schemas) {
if (isNull(schemas)) {
throw new RestException(Response.Status.NOT_FOUND.getStatusCode(), "Schemas not found");
} else {
return GetAllVersionsSchemaResponse.builder()
.getSchemaResponses(schemas.stream()View on GitHub (pinned to 820761864e)
Solutions
- Inspect broker logs for the RuntimeException's cause (the IOException stack trace) to identify whether storage read or schema deserialization failed.
- Verify schema storage health (BookKeeper ledgers backing the schema store); restore from backup or re-upload the schema via PUT if entries are corrupted.
- Delete the bad schema version (deleteSchema) and re-register a valid schema to replace the corrupt entry.
- If it reproduces on a healthy cluster, capture broker/client versions and file an issue — wrapping an IOException as a bare RuntimeException is a known rough edge in this code path.
Example fix
// server-side hardening in convertSchemaAndMetadataToGetSchemaResponse
// before
} catch (IOException conversionError) {
throw new RuntimeException(conversionError);
}
// after
} catch (IOException conversionError) {
throw new RestException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"Failed to convert stored schema: " + conversionError.getMessage());
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify schema is readable before use
SchemaAndMetadata meta = /* fetched */;
if (meta != null && meta.schema != null && meta.schema.getData() != null) {
String data = new String(meta.schema.getData(), StandardCharsets.UTF_8);
if (data.isEmpty()) throw new IllegalStateException("Empty stored schema data");
} Type guard
static boolean isReadableSchema(SchemaAndMetadata m) {
return m != null && m.schema != null && m.schema.getData() != null && m.schema.getData().length > 0;
} Try / catch
try {
GetSchemaResponse resp = admin.schemas().getSchemaInfo(topic);
} catch (PulsarAdminException e) {
if (e.getStatusCode() >= 500) {
log.error("Broker failed converting stored schema; check storage health", e);
}
throw e;
} catch (RuntimeException e) {
log.error("Unexpected schema conversion failure: {}", e.getMessage(), e);
throw e;
} Prevention
- Monitor BookKeeper/schema-store health; most 500s here trace to storage read errors.
- Re-upload schemas after broker version migrations if conversion paths changed.
- Set alerts on admin GET schema 500s to catch corruption early.
- Avoid deleting underlying ledgers backing the schema store.
When it happens
Trigger: GET /admin/v2/schemas/{tenant}/{namespace}/{topic}/schema (or /latest, /versions/{version}) when the broker fails to deserialize or convert the stored schema definition — e.g. corrupt schema ledger/bookkeeper entry or an internal conversion path that declares IOException.
Common situations: Underlying schema storage corruption (BookKeeper ledger truncation/loss); schema written by an incompatible older broker; transient storage read failures surfacing as conversion IOExceptions; a schema type registered server-side that the conversion code path can't handle.
Related errors
- RestException(conversionError)
- Failed to get permissions
- Schema compatibility strategy is ALWAYS_INCOMPATIBLE
- Incompatible schema: exists schema type %s, new schema type
- domain() invoked from wrong resource
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/4e4da8e15ab34309.
Report an issue: GitHub.