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

  1. Inspect broker logs for the RuntimeException's cause (the IOException stack trace) to identify whether storage read or schema deserialization failed.
  2. Verify schema storage health (BookKeeper ledgers backing the schema store); restore from backup or re-upload the schema via PUT if entries are corrupted.
  3. Delete the bad schema version (deleteSchema) and re-register a valid schema to replace the corrupt entry.
  4. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/4e4da8e15ab34309. Report an issue: GitHub.