apache/druid · error · MSQException

UnknownFault.forException(e)

Error message

UnknownFault.forException(e)

What it means

IndexerTableInputSpecSlicer.getTimeline fetches the datasource timeline via coordinator/broker HTTP calls; an IOException during those calls is wrapped as MSQException(e, UnknownFault.forException(e)). It means the table input spec could not learn which segments back the datasource — a connectivity/IO failure against Coordinator or metadata, not a query-logic problem.

Solutions

  1. Check coordinator availability and health endpoint; retry the query after the coordinator recovers
  2. Verify network/DNS/TLS connectivity from the MSQ controller task to the coordinator
  3. Inspect druid.coordination.coordinator config for wrong host/port
  4. If recurring during upgrades, stagger coordinator restarts away from query launches

Example fix

// handling at call site
try {
  timeline = slicer.timeline(spec);
} catch (MSQException e) {
  if (e.getCause() instanceof IOException) {
    log.warn("Coordinator unreachable, retrying: %s", e.getCause().getMessage());
    // retry with backoff
  } else { throw e; }
}
Defensive patterns

Strategy: retry

Validate before calling

// check coordinator health before submitting table-input MSQ queries
HttpResponse r = get(coordinatorUrl + "/status/health");
if (r.statusCode() != 200) throw new IllegalStateException("Coordinator unhealthy");

Try / catch

try {
  timeline = slicer.timeline(spec);
} catch (MSQException e) {
  if (e.getCause() instanceof IOException) {
    retryWithBackoff(); // coordinator may be restarting
  } else { throw e; }
}

Prevention

When it happens

Trigger: Coordinator unreachable or returning connection reset during the timeline request; interrupted HTTP call (IOException) while reading segment metadata; TLS or DNS failures resolving the coordinator host.

Common situations: Coordinator restart or rolling upgrade while an MSQ query starts; network policy blocking peon→coordinator traffic; misconfigured druid.host / coordinator URL in the cluster config.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at multi-stage-query/src/main/java/org/apache/druid/msq/indexing/IndexerTableInputSpecSlicer.java:235

    // If the task is operating with a REPLACE lock,
    // any segment created after the lock was acquired for its interval will not be considered.
    final Collection<DataSegment> publishedUsedSegments;
    try {
      // Additional check as the task action does not accept empty intervals
      if (intervals.isEmpty()) {
        publishedUsedSegments = Collections.emptySet();
      } else {
        publishedUsedSegments = taskActionClient.submit(
            new RetrieveUsedSegmentsAction(
                dataSource,
                intervals,
                SegmentDetail.none() // Even LoadSpec is not needed, because workers fetch them from the Coordinator.
            )
        );
      }
    }
    catch (IOException e) {
      throw new MSQException(e, UnknownFault.forException(e));
    }

    int realtimeCount = 0;

    // Deduplicate segments, giving preference to published used segments.
    // We do this so that if any segments have been handed off in between the two metadata calls above,
    // we directly fetch it from deep storage.
    Set<DataSegment> unifiedSegmentView = new HashSet<>(publishedUsedSegments);

    // Iterate over the realtime segments and segments loaded on the historical
    for (ImmutableSegmentLoadInfo segmentLoadInfo : realtimeAndHistoricalSegments) {
      Set<DruidServerMetadata> servers = segmentLoadInfo.getServers();
      // Filter out only realtime servers. We don't want to query historicals for now, but we can in the future.
      // This check can be modified then.
      Set<DruidServerMetadata> realtimeServerMetadata
          = servers.stream()
                   .filter(druidServerMetadata -> includeSegmentSource.getUsedServerTypes()
                                                                      .contains(druidServerMetadata.getType())

View on GitHub (pinned to 9b90983fd2)