apache/druid · critical · IllegalStateException

Different restrictions on table [%s]: previous policy [%s] a

Error message

Different restrictions on table [%s]: previous policy [%s] and new policy [%s]

What it means

A RestrictedDataSource already carries a policy; re-running withPolicies must produce the identical policy (or an empty/NoRestrictionPolicy). If the newly resolved policy differs from the existing one, the two query-planning passes disagree about restrictions, so Druid throws ISE to prevent weakening row-level security mid-flight.

Source

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

  {
    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.
    policyEnforcer.validateOrElseThrow(base, policy);
    return this;
  }

  @Override
  public String toString()
  {
    return "RestrictedDataSource{" +
           "base=" + base +
           ", policy=" + policy + "}";

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Ensure the Policy implementation overrides equals()/hashCode() so logically identical policies compare equal
  2. Make the PolicyEnforcer deterministic for the same table and credentials
  3. Investigate why policy metadata differs between the two evaluation passes (stale cache, config drift)
  4. If the restriction legitimately changed, re-plan the query from scratch rather than re-checking

Example fix

// before
class TenantPolicy implements Policy { private final String tenant; /* no equals */ }
// after
@Override
public boolean equals(Object o) {
  return o instanceof TenantPolicy && ((TenantPolicy) o).tenant.equals(tenant);
}
@Override
public int hashCode() { return tenant.hashCode(); }
Defensive patterns

Strategy: try-catch

Validate before calling

Optional<Policy> next = policyMap.getOrDefault(restrictedDs.getName(), Optional.empty());
if (next.isPresent() && !(next.get() instanceof NoRestrictionPolicy) && !next.get().equals(currentPolicy)) {
  throw new IllegalStateException("Policy drift between planning passes for " + restrictedDs.getName());
}

Type guard

boolean policiesCompatible(Policy a, Optional<Policy> b) {
  return b.isEmpty() || b.get() instanceof NoRestrictionPolicy || b.get().equals(a);
}

Try / catch

try {
  DataSource ds = restricted.withPolicies(policyMap, enforcer);
} catch (IllegalStateException e) {
  // abort query; policy metadata changed mid-flight — re-plan
}

Prevention

When it happens

Trigger: Calling withPolicies with a policyMap whose entry for the table is a Policy that is neither empty/NoRestrictionPolicy nor equal to the existing policy; non-deterministic PolicyEnforcer returning different policies between the initial planning and re-check pass.

Common situations: Policy objects not implementing equals() consistently (custom Policy classes); policy metadata changing between query submission and execution; different enforcer instances configured differently across cluster nodes.

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/51190cac95e693cc. Report an issue: GitHub.