apache/druid · error · IllegalArgumentException
Cluster-level rules cannot be empty.
Error message
Cluster-level rules cannot be empty.
What it means
SQLMetadataRuleManager.overrideRule() throws IllegalArgumentException when an empty rule list is supplied for the special cluster-level (default) datasource. The cluster-level rule set is the fallback for all datasources and must contain at least one rule; clearing it would leave the cluster with no default load behavior.
Solutions
- Provide at least one rule (e.g. a default loadForever rule) when overriding the cluster-level datasource.
- Skip the default datasource in bulk-clear operations, or target specific datasources instead.
- If the intent is to reset rules, replace with a sensible default rule set rather than an empty one.
Example fix
// before
ruleManager.overrideRule(config.getDefaultRule(), Collections.emptyList(), auditInfo); // rejected
// after
ruleManager.overrideRule(
config.getDefaultRule(),
Collections.singletonList(new PeriodLoadRule(Period.days(7), null, null, null, ImmutableMap.of())),
auditInfo); Defensive patterns
Strategy: validation
Validate before calling
if (dataSource.equals(config.getDefaultRule()) && rules.isEmpty()) {
throw new IllegalArgumentException("Cluster-level rules must contain at least one rule");
} Type guard
boolean isLegalClusterOverride(String ds, List<Rule> rules) {
return !(config.getDefaultRule().equals(ds) && rules != null && rules.isEmpty());
} Try / catch
try {
ruleManager.overrideRule(dataSource, rules, auditInfo);
} catch (IllegalArgumentException e) {
log.error(e, "Cannot clear cluster-level rules: %s", e.getMessage());
} Prevention
- Exclude the default datasource from bulk rule-clearing scripts
- Maintain a known-good default rule set for resets
- Validate rule payloads against the datasource being targeted
When it happens
Trigger: POSTing an empty rules array to the cluster-level default datasource rules endpoint (dataSource equal to config.getDefaultRule(), e.g. '_default'); programmatic overrideRule("_default", Collections.emptyList(), auditInfo).
Common situations: Scripts that bulk-clear rules for 'all datasources' without excluding the default datasource; UI/tooling assuming empty rules are always acceptable.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- given update for lookup
- Limit must be greater than zero!
- Rules cannot be null.
- A local input source accepts only one of
- A local input source can set parameter
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/8c408eb56be611ee.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/main/java/org/apache/druid/metadata/SQLMetadataRuleManager.java:316
} else {
log.error(e, "Exception while polling for rules");
}
}
}
@Override
public RetentionRulesSnapshot getRulesSnapshot()
{
return rulesSnapshot.get();
}
@Override
public boolean overrideRule(final String dataSource, final List<Rule> newRules, final AuditInfo auditInfo)
{
if (newRules == null) {
throw new IAE("Rules cannot be null.");
} else if (newRules.isEmpty() && config.getDefaultRule().equals(dataSource)) {
throw new IAE("Cluster-level rules cannot be empty.");
}
final String ruleString;
try {
ruleString = jsonMapper.writeValueAsString(newRules);
// uses getAllRules over getOverrideRules to allow proper audit trail when setting explicit [] override rules for
// a datasource with no existing overrides.
if (ruleString.equals(jsonMapper.writeValueAsString(rulesSnapshot.get().getAllRules().get(dataSource)))) {
log.info("Retention rules unchanged for datasource[%s] with rules[%s]", dataSource, ruleString);
return true;
}
log.info("Updating datasource[%s] with rules[%s] as per [%s]", dataSource, ruleString, auditInfo);
}
catch (JsonProcessingException e) {
log.error(e, "Unable to write rules as string for datasource[%s]", dataSource);
return false;
}
synchronized (lock) {View on GitHub (pinned to 9b90983fd2)