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
- Set the 'urls' property in the schemaRegistry decoder config to a list of schema registry endpoints, e.g. ["http://registry:8081"]
- If migrating from 'url', move the value into 'urls' (the old key still works but is deprecated)
- 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
- Always set 'urls' with at least one registry endpoint in the inputFormat spec
- Lint ingestion specs (JSON schema validation) before submitting supervisors
- Migrate off the deprecated 'url' key so a single config path is validated
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
- A valid tlsPort needs to specified when druid.enableTlsPort…
- argument should be an identifier expression. Use array()…
- At least one of the druid.enablePlaintextPort or…
- At least one task runner must be enabled
- At most one of 'druid.broker.segment.watchedTiers' and…
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)