apache/druid · error · ISE

If configured, 'druid.broker.segment.watchedTiers' must be…

Error message

If configured, 'druid.broker.segment.watchedTiers' must be non-empty

What it means

BrokerServerView.validateSegmentWatcherConfig() rejects a watchedTiers config that is present but empty. Watching zero tiers is meaningless (no segments would ever be watched), so the property must either be absent or contain at least one tier name.

Solutions

  1. Either remove the druid.broker.segment.watchedTiers property entirely (watch all tiers) or list at least one tier, e.g. watchedTiers=hot.
  2. Verify tier names match those the historicals announce (druid.server.tier).
  3. Restart the broker after the config change.

Example fix

// before
druid.broker.segment.watchedTiers=
// after
druid.broker.segment.watchedTiers=hot,_default_tier
Defensive patterns

Strategy: validation

Validate before calling

List<String> watched = getTierList(props, "druid.broker.segment.watchedTiers");
if (watched != null && watched.isEmpty()) {
  throw new IllegalStateException("watchedTiers configured but empty; remove it or list tiers");
}

Prevention

When it happens

Trigger: Setting druid.broker.segment.watchedTiers= (empty value) or druid.broker.segment.watchedTiers=[] in broker runtime.properties, producing a non-null empty list passed to BrokerServerView.

Common situations: Clearing the tier list by deleting the values but keeping the property; templated configs emitting empty keys; mistyping intended tier names then removing them but not the property.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/b00a876604ab2364. Report an issue: GitHub.

Appendix: source

Thrown at server/src/main/java/org/apache/druid/client/BrokerServerView.java:242

   * <ul>
   *   <li>At most one of watchedTiers or ignoredTiers can be set</li>
   *   <li>If set, watchedTiers must be non-empty</li>
   *   <li>If set, ignoredTiers must be non-empty</li>
   * </ul>
   */
  private void validateSegmentWatcherConfig(BrokerSegmentWatcherConfig watcherConfig)
  {
    if (watcherConfig.getWatchedTiers() != null
        && watcherConfig.getIgnoredTiers() != null) {
      throw new ISE(
          "At most one of 'druid.broker.segment.watchedTiers' "
          + "and 'druid.broker.segment.ignoredTiers' can be configured."
      );
    }

    if (watcherConfig.getWatchedTiers() != null
        && watcherConfig.getWatchedTiers().isEmpty()) {
      throw new ISE("If configured, 'druid.broker.segment.watchedTiers' must be non-empty");
    }

    if (watcherConfig.getIgnoredTiers() != null
        && watcherConfig.getIgnoredTiers().isEmpty()) {
      throw new ISE("If configured, 'druid.broker.segment.ignoredTiers' must be non-empty");
    }
  }

  private QueryableDruidServer addServer(DruidServer server)
  {
    QueryableDruidServer retVal = druidClientFactory.make(server);
    QueryableDruidServer exists = clients.put(server.getName(), retVal);
    if (exists != null) {
      log.warn("QueryRunner for server[%s] already exists!? Well it's getting replaced", server);
    }

    return retVal;
  }

View on GitHub (pinned to 9b90983fd2)