apache/druid · error · IllegalArgumentException

Cannot query dataSource locally

Error message

Cannot query dataSource locally: %s

What it means

LocalQuerySegmentWalker can only execute queries whose dataSource is locally runnable (e.g. table/lookup sources). If the query's DataSource cannot run on the local walker (like a query dataSource or join type the local walker does not support), it throws IAE naming the dataSource.

Solutions

  1. Run the query on the broker/router with the distributed walker instead of the local one
  2. Rewrite the query so its dataSource is a plain table source (flatten the subquery or run subqueries separately)
  3. Check ExecutionVertex.canRunQueryUsingLocalWalker semantics and adapt the query shape
  4. Enable/extend wrangler support for the dataSource type if running in an embedded/test cluster

Example fix

// before
query with dataSource = new QueryDataSource(nestedGroupBy)
submitted directly to local walker
// after
run nestedGroupBy first, then use its results as a table/inline dataSource:
Query<?> inner = ...; // execute separately
TableDataSource outer = new TableDataSource("myTable");
Defensive patterns

Strategy: try-catch

Validate before calling

ExecutionVertex ev = ExecutionVertex.of(query);
if (!ev.canRunQueryUsingLocalWalker()) {
  throw new IllegalArgumentException("dataSource not locally runnable: " + ev.getBaseDataSource());
}

Type guard

boolean locallyRunnable(Query<?> q) {
  return ExecutionVertex.of(q).canRunQueryUsingLocalWalker();
}

Try / catch

try {
  return walker.getQueryRunnerForIntervals(query, intervals);
} catch (IAE e) {
  if (e.getMessage().startsWith("Cannot query dataSource locally")) {
    log.warn("Re-routing query to distributed broker: %s", e.getMessage());
    return distributedBroker.getQueryRunnerForIntervals(query, intervals);
  }
  throw e;
}

Prevention

When it happens

Trigger: Issuing a native query with a QueryDataSource/union/unsupported join directly against a broker configured to use the local segment walker; querying a dataSource type (e.g. inline in some configs) not supported by the wrangler.

Common situations: Submitting nested native queries to a node that only has the local walker (no distributed broker); tests or embedded cluster setups running queries whose dataSource requires MSQ or a router.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at server/src/main/java/org/apache/druid/server/LocalQuerySegmentWalker.java:91

      PolicyEnforcer policyEnforcer,
      ServiceEmitter emitter
  )
  {
    this.conglomerate = conglomerate;
    this.segmentWrangler = segmentWrangler;
    this.joinableFactoryWrapper = joinableFactoryWrapper;
    this.scheduler = scheduler;
    this.policyEnforcer = policyEnforcer;
    this.emitter = emitter;
  }

  @Override
  public <T> QueryRunner<T> getQueryRunnerForIntervals(final Query<T> query, final Iterable<Interval> intervals)
  {
    ExecutionVertex ev = ExecutionVertex.of(query);

    if (!ev.canRunQueryUsingLocalWalker()) {
      throw new IAE("Cannot query dataSource locally: %s", ev.getBaseDataSource());
    }

    // wrap in ReferenceCountingSegment, these aren't currently managed by SegmentManager so reference tracking doesn't
    // matter, but at least some or all will be in a future PR
    final Iterable<Optional<Segment>> segments =
        FunctionalIterable.create(segmentWrangler.getSegmentsForIntervals(ev.getBaseDataSource(), intervals))
                          .transform(ReferenceCountedSegmentProvider::unmanaged);

    final AtomicLong cpuAccumulator = new AtomicLong(0L);

    final SegmentMapFunction segmentMapFn = ev.createSegmentMapFunction(policyEnforcer);

    final QueryRunnerFactory<T, Query<T>> queryRunnerFactory = conglomerate.findFactory(query);
    final QueryRunner<T> baseRunner = queryRunnerFactory.mergeRunners(
        DirectQueryProcessingPool.INSTANCE,
        () -> StreamSupport.stream(segments.spliterator(), false)
                           .map(s -> segmentMapFn.apply(s).orElseThrow())
                           .map(queryRunnerFactory::createRunner).iterator()

View on GitHub (pinned to 9b90983fd2)