provectus/kafka-ui · error · ValidationException
Neither default, not per-topic descriptors defined for
Error message
Neither default, not per-topic descriptors defined for ${name()} serde What it means
ProtobufFileSerde requires at least one protobuf descriptor source: a default message descriptor, a default key descriptor, or per-topic descriptor maps. ProtobufFileSerde.configure throws ValidationException when the Configuration has none of these, since the serde would have no way to decode messages.
Solutions
- Provide descriptorPaths plus a default message name, or per-topic message descriptor entries in the serde configuration
- Check property key spelling in application.yaml (e.g. protobufFileSerde.descriptorPaths, defaultMessageDescriptor)
- If only key or value needs protobuf decoding, set at least the relevant descriptor map
Example fix
// before
serde:
protobufFileSerde: {}
// after
serde:
protobufFileSerde:
descriptorPaths: /etc/kafka-ui/proto/messages.proto
defaultMessageDescriptor: MyMessage Defensive patterns
Strategy: validation
Validate before calling
boolean hasDescriptors = defaultMsg != null || defaultKeyMsg != null || (msgMap != null && !msgMap.isEmpty()) || (keyMap != null && !keyMap.isEmpty()); if (!hasDescriptors) throw new IllegalArgumentException("protobufFileSerde needs at least one descriptor"); Type guard
boolean protobufConfigUsable(Configuration c) { return c.defaultMessageDescriptor() != null || c.defaultKeyMessageDescriptor() != null || !c.messageDescriptorMap().isEmpty() || !c.keyMessageDescriptorMap().isEmpty(); } Try / catch
try { serde.autoConfigure(props); } catch (ValidationException e) { log.error("protobuf serde misconfigured: {}", e.getMessage()); } Prevention
- Always set descriptorPaths plus a default or per-topic message name
- Dry-run serde config in a dev cluster first
- Validate YAML keys against documented protobufFileSerde properties
When it happens
Trigger: configure(configuration) called (directly or via autoConfigure) with defaultMessageDescriptor, defaultKeyMessageDescriptor, messageDescriptorMap and keyMessageDescriptorMap all null/empty.
Common situations: Enabling the protobuf-file serde without providing descriptor paths or message names in cluster serde properties; a typo making all descriptor properties fail to resolve; empty per-topic map entries.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- 'filePath' property not set for custom serde
- Serde can't be applied for ' ' topic's serialization
- Serde ' ' can't be applied to ' ' topic
- Application config isn't valid. Cluster names should be…
- Application config isn't valid. Two clusters can't have the…
AI-assisted analysis of provectus/kafka-ui@83b5a60cc0 (2026-09-08).
Data as JSON: /api/errors/9b97d90dd1d4cfdf.
Report an issue: GitHub.
Appendix: source
Thrown at kafka-ui-api/src/main/java/com/provectus/kafka/ui/serdes/builtin/ProtobufFileSerde.java:111
public void autoConfigure(PropertyResolver kafkaClusterProperties,
PropertyResolver globalProperties) {
configure(Configuration.create(kafkaClusterProperties));
}
@Override
public void configure(PropertyResolver serdeProperties,
PropertyResolver kafkaClusterProperties,
PropertyResolver globalProperties) {
configure(Configuration.create(serdeProperties));
}
@VisibleForTesting
void configure(Configuration configuration) {
if (configuration.defaultMessageDescriptor() == null
&& configuration.defaultKeyMessageDescriptor() == null
&& configuration.messageDescriptorMap().isEmpty()
&& configuration.keyMessageDescriptorMap().isEmpty()) {
throw new ValidationException("Neither default, not per-topic descriptors defined for " + name() + " serde");
}
this.defaultMessageDescriptor = configuration.defaultMessageDescriptor();
this.defaultKeyMessageDescriptor = configuration.defaultKeyMessageDescriptor();
this.descriptorPaths = configuration.descriptorPaths();
this.messageDescriptorMap = configuration.messageDescriptorMap();
this.keyMessageDescriptorMap = configuration.keyMessageDescriptorMap();
}
@Override
public Optional<String> getDescription() {
return Optional.empty();
}
private Optional<Descriptor> descriptorFor(String topic, Target type) {
return type == Target.KEY
?
Optional.ofNullable(keyMessageDescriptorMap.get(topic))
.or(() -> Optional.ofNullable(defaultKeyMessageDescriptor))View on GitHub (pinned to 83b5a60cc0)