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
- Ensure the Policy implementation overrides equals()/hashCode() so logically identical policies compare equal
- Make the PolicyEnforcer deterministic for the same table and credentials
- Investigate why policy metadata differs between the two evaluation passes (stale cache, config drift)
- 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
- Implement equals()/hashCode() on all custom Policy classes
- Keep policy metadata stable across the lifetime of a query
- Use one deterministic enforcer configuration cluster-wide
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
- Policy can't be null for RestrictedDataSource
- Missing policy check result for table [%s]
- Task type [%s], does not support input source based security
- Cannot handle dataSource [%s]
- Must have exactly one child
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/51190cac95e693cc.
Report an issue: GitHub.