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

  1. Provide descriptorPaths plus a default message name, or per-topic message descriptor entries in the serde configuration
  2. Check property key spelling in application.yaml (e.g. protobufFileSerde.descriptorPaths, defaultMessageDescriptor)
  3. 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

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


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)