{"record":{"id":"3f423fd96ebe5500","repo":"apache/druid","slug":"missing-policy-check-result-for-table-s","errorCode":null,"errorMessage":"Missing policy check result for table [%s]","messagePattern":"Missing policy check result for table \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/RestrictedDataSource.java","lineNumber":143,"sourceCode":"\n  @Override\n  public SegmentMapFunction createSegmentMapFunction(Query query)\n  {\n    return base.createSegmentMapFunction(query).thenMap(segment -> new RestrictedSegment(segment, policy));\n  }\n\n  @Nullable\n  @Override\n  public SegmentPruner createSegmentPruner()\n  {\n    return policy.createSegmentPruner();\n  }\n\n  @Override\n  public DataSource withPolicies(Map<String, Optional<Policy>> policyMap, PolicyEnforcer policyEnforcer)\n  {\n    if (!policyMap.containsKey(base.getName())) {\n      throw new ISE(\"Missing policy check result for table [%s]\", base.getName());\n    }\n\n    Optional<Policy> newPolicy = policyMap.getOrDefault(base.getName(), Optional.empty());\n    if (newPolicy.isEmpty() || newPolicy.get() instanceof NoRestrictionPolicy) {\n      // allow empty policy, which means no restriction.\n      // druid-internal calls with NoRestrictionPolicy: allow\n    } else if (newPolicy.get().equals(policy)) {\n      // same policy: allow\n    } else {\n      throw new ISE(\n          \"Different restrictions on table [%s]: previous policy [%s] and new policy [%s]\",\n          base.getName(),\n          policy,\n          newPolicy.get()\n      );\n    }\n    // The only happy path is, newPolicy is NoRestrictionPolicy, which means this comes from an anthenticated and\n    // authorized druid-internal request.","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/RestrictedDataSource.java#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the PolicyEnforcer to emit an entry for every input table, using Optional.empty() for unrestricted tables","Verify the table name used as the map key matches base.getName() (check for aliasing/renaming)","Wrap the enforcer to add missing keys as Optional.empty() before calling withPolicies"],"exampleFix":"// before\nMap<String, Optional<Policy>> results = enforcer.getPolicies(tables.subList(0, 1));\nrestricted.withPolicies(results, enforcer);\n// after\nMap<String, Optional<Policy>> results = enforcer.getPolicies(tables); // entry for every table\nif (!results.containsKey(restricted.getName())) {\n  results.put(restricted.getName(), Optional.empty());\n}\nrestricted.withPolicies(results, enforcer);","handlingStrategy":"try-catch","validationCode":"if (!policyMap.containsKey(restrictedDs.getName())) {\n  throw new IllegalStateException(\"Enforcer missing result for table \" + restrictedDs.getName());\n}","typeGuard":null,"tryCatchPattern":"try {\n  DataSource ds = restricted.withPolicies(policyMap, enforcer);\n} catch (IllegalStateException e) {\n  // re-run enforcer over all tables and retry once\n}","preventionTips":["Require PolicyEnforcer implementations to return results for every input table","Key policy maps by the exact datasource/table name used by Druid","Test custom enforcers with multi-table queries"],"tags":["druid","policy","datasource","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}