apache/druid · error · IllegalStateException

Missing policy check result for table [%s]

Error message

Missing policy check result for table [%s]

What it means

When a query containing a RestrictedDataSource flows through policy re-checking, withPolicies requires the policyMap to contain an entry for the base table name — even if the entry is Optional.empty(). A missing key means the enforcer never evaluated that table, which Druid treats as an internal invariant violation (ISE) rather than silently dropping restrictions.

Source

Thrown at processing/src/main/java/org/apache/druid/query/RestrictedDataSource.java:143

  @Override
  public SegmentMapFunction createSegmentMapFunction(Query query)
  {
    return base.createSegmentMapFunction(query).thenMap(segment -> new RestrictedSegment(segment, policy));
  }

  @Nullable
  @Override
  public SegmentPruner createSegmentPruner()
  {
    return policy.createSegmentPruner();
  }

  @Override
  public DataSource withPolicies(Map<String, Optional<Policy>> policyMap, PolicyEnforcer policyEnforcer)
  {
    if (!policyMap.containsKey(base.getName())) {
      throw new ISE("Missing policy check result for table [%s]", base.getName());
    }

    Optional<Policy> newPolicy = policyMap.getOrDefault(base.getName(), Optional.empty());
    if (newPolicy.isEmpty() || newPolicy.get() instanceof NoRestrictionPolicy) {
      // allow empty policy, which means no restriction.
      // druid-internal calls with NoRestrictionPolicy: allow
    } else if (newPolicy.get().equals(policy)) {
      // same policy: allow
    } else {
      throw new ISE(
          "Different restrictions on table [%s]: previous policy [%s] and new policy [%s]",
          base.getName(),
          policy,
          newPolicy.get()
      );
    }
    // The only happy path is, newPolicy is NoRestrictionPolicy, which means this comes from an anthenticated and
    // authorized druid-internal request.

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Fix the PolicyEnforcer to emit an entry for every input table, using Optional.empty() for unrestricted tables
  2. Verify the table name used as the map key matches base.getName() (check for aliasing/renaming)
  3. Wrap the enforcer to add missing keys as Optional.empty() before calling withPolicies

Example fix

// before
Map<String, Optional<Policy>> results = enforcer.getPolicies(tables.subList(0, 1));
restricted.withPolicies(results, enforcer);
// after
Map<String, Optional<Policy>> results = enforcer.getPolicies(tables); // entry for every table
if (!results.containsKey(restricted.getName())) {
  results.put(restricted.getName(), Optional.empty());
}
restricted.withPolicies(results, enforcer);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!policyMap.containsKey(restrictedDs.getName())) {
  throw new IllegalStateException("Enforcer missing result for table " + restrictedDs.getName());
}

Try / catch

try {
  DataSource ds = restricted.withPolicies(policyMap, enforcer);
} catch (IllegalStateException e) {
  // re-run enforcer over all tables and retry once
}

Prevention

When it happens

Trigger: Calling withPolicies(policyMap, enforcer) where policyMap lacks a key equal to base.getName(); a PolicyEnforcer that only populates results for tables it considers relevant, skipping restricted tables.

Common situations: Custom PolicyEnforcer returning a partial result map; table renamed or aliased so the enforcer keyed results by a different name; running through systems (e.g. druid-internal MSQ/centralized datasource logic) that rebuild the policy map from a subset of tables.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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