apache/druid · error · IllegalStateException

Cannot handle datasource

Error message

Cannot handle datasource: %s

What it means

SinkQuerySegmentWalker serves queries only for the single datasource its sinks belong to. getQueryRunnerForSegments verifies the query's base table data source name matches the walker's datasource and throws this ISE otherwise — a sanity check that the query is targeting the table this walker is meant to handle.

Solutions

  1. Query only the exact datasource served by the realtime task; perform joins on the broker/historical layer, not at the sink walker
  2. Check that the query's base table name exactly matches the task's datasource (name/case)
  3. Fix broker query distribution so multi-datasource queries are not routed to single-datasource peons

Example fix

// before
// SQL: SELECT ... FROM realtimeDs JOIN otherTable ...  routed to realtimeDs peon -> throws
// after
// let broker handle joins; peon only receives single-table scans over realtimeDs
Defensive patterns

Strategy: validation

Validate before calling

if (!query.getDataSource().getTableNames().equals(Collections.singletonList(expectedDatasource))) { /* route elsewhere */ }

Type guard

boolean isSingleTableQuery = query.getDataSource() instanceof TableDataSource && ((TableDataSource) query.getDataSource()).getName().equals(datasource);

Try / catch

try { return walker.getQueryRunnerForSegments(query, specs); } catch (ISE e) { log.warn("query datasource mismatch; delegating to broker"); return nextRunner.getQueryRunnerForSegments(query, specs); }

Prevention

When it happens

Trigger: A query whose dataSource resolves to a different base table (e.g. join of another table, lookup, subquery, or inline/query datasources) reaches a walker created for a specific datasource name.

Common situations: Broker routing a join or subquery to a realtime task's peon that only owns one datasource; SQL queries joining a realtime datasource with another table; misconfigured query routing or datasource naming mismatch (case differences).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java:181

                        holder.getVersion(),
                        chunk.getChunkNumber()
                    )
                )
        );

    return getQueryRunnerForSegments(query, specs);
  }

  @Override
  public <T> QueryRunner<T> getQueryRunnerForSegments(final Query<T> query, final Iterable<SegmentDescriptor> specs)
  {
    ExecutionVertex ev = ExecutionVertex.of(query);
    // We only handle one particular dataSource. Make sure that's what we have, then ignore from here on out.
    final DataSource dataSourceFromQuery = query.getDataSource();

    // Sanity check: make sure the query is based on the table we're meant to handle.
    if (!ev.getBaseTableDataSource().getName().equals(dataSource)) {
      throw new ISE("Cannot handle datasource: %s", dataSourceFromQuery);
    }

    final QueryRunnerFactory<T, Query<T>> factory = conglomerate.findFactory(query);
    if (factory == null) {
      throw new ISE("Unknown query type[%s].", query.getClass());
    }

    final QueryToolChest<T, Query<T>> toolChest = factory.getToolchest();
    final boolean skipIncrementalSegment = query.context().getBoolean(CONTEXT_SKIP_INCREMENTAL_SEGMENT, false);
    final AtomicLong cpuTimeAccumulator = new AtomicLong(0L);

    // Make sure this query type can handle the subquery, if present.
    if ((dataSourceFromQuery instanceof QueryDataSource)
        && !toolChest.canPerformSubquery(((QueryDataSource) dataSourceFromQuery).getQuery())) {
      throw new ISE("Cannot handle subquery: %s", dataSourceFromQuery);
    }

    // segmentMapFn maps each base Segment into a joined Segment if necessary.

View on GitHub (pinned to 9b90983fd2)