apache/druid · error · IllegalArgumentException

Expected a TableDataSource, got data source type [%s]

Error message

Expected a TableDataSource, got data source type [%s]

What it means

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.

Source

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

  public Policy getPolicy()
  {
    return policy;
  }

  RestrictedDataSource(TableDataSource base, Policy policy)
  {
    this.base = base;
    this.policy = policy;
  }

  @JsonCreator
  public static RestrictedDataSource create(
      @JsonProperty("base") DataSource base,
      @JsonProperty("policy") Policy policy
  )
  {
    if (!(base instanceof TableDataSource)) {
      throw new IAE("Expected a TableDataSource, got data source type [%s]", base.getClass());
    }
    if (Objects.isNull(policy)) {
      throw new IAE("Policy can't be null for RestrictedDataSource");
    }
    return new RestrictedDataSource((TableDataSource) base, policy);
  }

  @Override
  public Set<String> getTableNames()
  {
    return base.getTableNames();
  }

  @Override
  public List<DataSource> getChildren()
  {
    return ImmutableList.of(base);
  }

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Use a TableDataSource (a druid table) as the base for RestrictedDataSource.
  2. If you need to restrict a non-table source, apply the policy at a different layer or convert the source into a table first.
  3. Verify the serialized 'base' field resolves to type 'table' and not another DataSource subtype.
  4. Confirm no config templating accidentally replaces the base with a lookup/inline/query data source.

Example fix

// before
DataSource base = new LookupDataSource("my_lookup");
RestrictedDataSource rds = RestrictedDataSource.create(base, policy);
// after
DataSource base = new TableDataSource(TableDataSource.name("my_table"));
if (!(base instanceof TableDataSource)) {
  throw new IllegalArgumentException("RestrictedDataSource requires a table base");
}
RestrictedDataSource rds = RestrictedDataSource.create(base, policy);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(base instanceof TableDataSource)) {
  throw new IllegalArgumentException("RestrictedDataSource.create requires a TableDataSource base, got: "
      + base.getClass().getSimpleName());
}
if (policy == null) {
  throw new IllegalArgumentException("Policy must be non-null");
}

Type guard

static boolean isRestrictable(DataSource base) {
  return base instanceof TableDataSource;
}

Try / catch

try {
  RestrictedDataSource rds = RestrictedDataSource.create(base, policy);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("Expected a TableDataSource")) {
    throw new IllegalArgumentException("Configured base must be a druid table, was: "
        + base.getClass().getSimpleName(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling RestrictedDataSource.create(base, policy) — directly or via JSON deserialization of the @JsonProperty 'base' — with a base that is not a TableDataSource.

Common situations: 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.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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