apache/druid · error · IllegalArgumentException

SchemaRegistryBasedProtobufBytesDecoder requires non-null…

Error message

SchemaRegistryBasedProtobufBytesDecoder requires non-null URLs

What it means

SchemaRegistryBasedProtobufBytesDecoder builds a Confluent CachedSchemaRegistryClient from configured registry endpoints. The 'url' property is deprecated in favor of 'urls'; if neither yields a non-empty base URL list (both null/empty), the constructor throws IllegalArgumentException because it cannot create a registry client without endpoints.

Solutions

  1. Set the 'urls' property in the schemaRegistry decoder config to a list of schema registry endpoints, e.g. ["http://registry:8081"]
  2. If migrating from 'url', move the value into 'urls' (the old key still works but is deprecated)
  3. Validate the ingestion spec's inputFormat.schemaRegistry section before submitting the supervisor

Example fix

// before
"schemaRegistry": {"urls": []}
// after
"schemaRegistry": {"urls": ["http://schema-registry:8081"]}
Defensive patterns

Strategy: validation

Validate before calling

Object urls = spec.path("schemaRegistry.urls");
Object url = spec.path("schemaRegistry.url");
boolean empty = (urls == null || ((List<?>) urls).isEmpty()) && (url == null || url.toString().isEmpty());
if (empty) throw new IllegalArgumentException("schemaRegistry requires non-empty 'urls'");

Type guard

boolean hasRegistryUrls(Map<String, Object> schemaRegistry) {
  List<?> urls = (List<?>) schemaRegistry.get("urls");
  String url = (String) schemaRegistry.get("url");
  return (urls != null && !urls.isEmpty()) || (url != null && !url.isEmpty());
}

Try / catch

try {
  SchemaRegistryBasedProtobufBytesDecoder decoder =
      mapper.convertValue(schemaRegistryCfg, SchemaRegistryBasedProtobufBytesDecoder.class);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("requires non-null URLs")) {
    throw new SpecValidationException("Set schemaRegistry.urls in the input format spec");
  }
  throw e;
}

Prevention

When it happens

Trigger: Instantiating the decoder (e.g. via JSON deserialization of the kafka ingestion spec) with neither 'urls' set nor the deprecated 'url' set — both absent or empty strings/lists.

Common situations: Missing schemaRegistry config in the input format spec after migration from 'url' to 'urls'; a typo like 'url1' or an empty urls: [] in the supervisor spec.

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 apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/d1a60cf6e576fa16. Report an issue: GitHub.

Appendix: source

Thrown at extensions-core/protobuf-extensions/src/main/java/org/apache/druid/data/input/protobuf/SchemaRegistryBasedProtobufBytesDecoder.java:86

      @JsonProperty("config") @Nullable Map<String, Object> config,
      @JsonProperty("headers") @Nullable Map<String, Object> headers,
      @JacksonInject @Json ObjectMapper jsonMapper
  )
  {
    this.url = url;
    this.capacity = capacity == null ? Integer.MAX_VALUE : capacity;
    this.urls = urls;
    this.config = config;
    this.headers = headers;
    this.jsonMapper = jsonMapper;

    if (StringUtils.isNotEmpty(this.url)) {
      LOGGER.warn("\"url\" is deprecated, use \"urls\" instead");
    }

    final var baseUrls = StringUtils.isNotEmpty(this.url) ? Collections.singletonList(this.url) : this.urls;
    if (baseUrls == null) {
      throw new IAE("SchemaRegistryBasedProtobufBytesDecoder requires non-null URLs");
    }

    this.registry = new CachedSchemaRegistryClient(
        baseUrls,
        this.capacity,
        Collections.singletonList(new ProtobufSchemaProvider()),
        DynamicConfigProviderUtils.extraConfigAndSetObjectMap(
            config,
            DRUID_DYNAMIC_CONFIG_PROVIDER_KEY,
            this.jsonMapper
        ),
        DynamicConfigProviderUtils.extraConfigAndSetStringMap(
            headers,
            DRUID_DYNAMIC_CONFIG_PROVIDER_KEY,
            this.jsonMapper
        )
    );
  }

View on GitHub (pinned to 9b90983fd2)