{"record":{"id":"fbf42372cd270db4","repo":"apache/druid","slug":"expected-a-tabledatasource-got-data-source-type","errorCode":null,"errorMessage":"Expected a TableDataSource, got data source type [%s]","messagePattern":"Expected a TableDataSource, got data source type \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/RestrictedDataSource.java","lineNumber":78,"sourceCode":"  public Policy getPolicy()\n  {\n    return policy;\n  }\n\n  RestrictedDataSource(TableDataSource base, Policy policy)\n  {\n    this.base = base;\n    this.policy = policy;\n  }\n\n  @JsonCreator\n  public static RestrictedDataSource create(\n      @JsonProperty(\"base\") DataSource base,\n      @JsonProperty(\"policy\") Policy policy\n  )\n  {\n    if (!(base instanceof TableDataSource)) {\n      throw new IAE(\"Expected a TableDataSource, got data source type [%s]\", base.getClass());\n    }\n    if (Objects.isNull(policy)) {\n      throw new IAE(\"Policy can't be null for RestrictedDataSource\");\n    }\n    return new RestrictedDataSource((TableDataSource) base, policy);\n  }\n\n  @Override\n  public Set<String> getTableNames()\n  {\n    return base.getTableNames();\n  }\n\n  @Override\n  public List<DataSource> getChildren()\n  {\n    return ImmutableList.of(base);\n  }","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/RestrictedDataSource.java#L60-L96","documentation":"RestrictedDataSource.create() wraps a base data source with a row-level access policy, but only TableDataSource bases are supported. If the supplied base is any other DataSource subtype (lookup, inline, union, query, etc.), it throws IAE naming the actual type. This is upfront type validation before constructing the restricted view.","triggerScenarios":"Calling RestrictedDataSource.create(base, policy) — directly or via JSON deserialization of the @JsonProperty 'base' — with a base that is not a TableDataSource.","commonSituations":"Authoring catalog/resource configs that apply row filters to non-table data sources; JSON round-trips producing an unexpected base type; copy-pasted configs that swap an inline or lookup source into a restricted-data-source block.","solutions":["Use a TableDataSource (a druid table) as the base for RestrictedDataSource.","If you need to restrict a non-table source, apply the policy at a different layer or convert the source into a table first.","Verify the serialized 'base' field resolves to type 'table' and not another DataSource subtype.","Confirm no config templating accidentally replaces the base with a lookup/inline/query data source."],"exampleFix":"// before\nDataSource base = new LookupDataSource(\"my_lookup\");\nRestrictedDataSource rds = RestrictedDataSource.create(base, policy);\n// after\nDataSource base = new TableDataSource(TableDataSource.name(\"my_table\"));\nif (!(base instanceof TableDataSource)) {\n  throw new IllegalArgumentException(\"RestrictedDataSource requires a table base\");\n}\nRestrictedDataSource rds = RestrictedDataSource.create(base, policy);","handlingStrategy":"type-guard","validationCode":"if (!(base instanceof TableDataSource)) {\n  throw new IllegalArgumentException(\"RestrictedDataSource.create requires a TableDataSource base, got: \"\n      + base.getClass().getSimpleName());\n}\nif (policy == null) {\n  throw new IllegalArgumentException(\"Policy must be non-null\");\n}","typeGuard":"static boolean isRestrictable(DataSource base) {\n  return base instanceof TableDataSource;\n}","tryCatchPattern":"try {\n  RestrictedDataSource rds = RestrictedDataSource.create(base, policy);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"Expected a TableDataSource\")) {\n    throw new IllegalArgumentException(\"Configured base must be a druid table, was: \"\n        + base.getClass().getSimpleName(), e);\n  }\n  throw e;\n}","preventionTips":["Validate the 'base' field type ('table') when parsing restricted-data-source configs","Add a config-schema check that rejects non-table bases before runtime","Test JSON deserialization of restricted data source configs with all base variants"],"tags":["druid","datasource","type-mismatch","row-level-security"],"backgroundTag":"incompatible-source-type","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"}