apache/druid · error · BadQueryContextException

Per-segment timeout [timeoutPerSegmentQuery] must be a non n

Error message

Per-segment timeout [timeoutPerSegmentQuery] must be a non negative value, but was [%d]

What it means

Druid validates the `timeoutPerSegmentQuery` query-context key, which caps the time spent on each per-segment query in scatter-gather execution. QueryContext.getPerSegmentTimeout throws BadQueryContextException when the value is negative, since a negative per-segment budget is meaningless. Like the other timeout checks, this guards deadline arithmetic downstream.

Source

Thrown at processing/src/main/java/org/apache/druid/query/QueryContext.java:568

              maxQueryTimeout
          )
      );
    }
  }

  public long getPerSegmentTimeout()
  {
    return getPerSegmentTimeout(QueryContexts.NO_TIMEOUT);
  }

  public long getPerSegmentTimeout(long defaultPerSegmentTimeout)
  {
    final long timeout = getLong(QueryContexts.PER_SEGMENT_TIMEOUT_KEY, defaultPerSegmentTimeout);
    if (timeout >= 0) {
      return timeout;
    }

    throw new BadQueryContextException(
        StringUtils.format(
            "Per-segment timeout [%s] must be a non negative value, but was [%d]",
            QueryContexts.PER_SEGMENT_TIMEOUT_KEY,
            timeout
        )
    );
  }

  public boolean usePerSegmentTimeout()
  {
    return getPerSegmentTimeout() != QueryContexts.NO_TIMEOUT;
  }

  public void verifyMaxScatterGatherBytes(long maxScatterGatherBytesLimit)
  {
    long curr = getLong(QueryContexts.MAX_SCATTER_GATHER_BYTES_KEY, 0);
    if (curr > maxScatterGatherBytesLimit) {
      throw new BadQueryContextException(

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set `timeoutPerSegmentQuery` in the query context to a non-negative millisecond value, or remove the key to use the default.
  2. Clamp any computed value: `long t = Math.max(0, computed);` before putting it in the context.
  3. Search the query-issuing code for negative sentinel values and replace them with omission of the key.
  4. Catch BadQueryContextException and log/return the offending context key and value.

Example fix

// before
context.put("timeoutPerSegmentQuery", -1L); // meant 'unlimited'
// after
// omit the key to use the default, or supply a positive value
context.put("timeoutPerSegmentQuery", 10_000L);
Defensive patterns

Strategy: validation

Validate before calling

Object tps = query.getContext().get("timeoutPerSegmentQuery");
if (tps instanceof Number && ((Number) tps).longValue() < 0) {
    throw new IllegalArgumentException("timeoutPerSegmentQuery must be non-negative, got " + tps);
}

Type guard

boolean isValidPerSegmentTimeout(Object v) {
    return !(v instanceof Number) || ((Number) v).longValue() >= 0;
}

Try / catch

try {
    client.query(query);
} catch (BadQueryContextException e) {
    if (e.getMessage().contains("Per-segment timeout")) {
        query.getContext().remove("timeoutPerSegmentQuery");
        client.query(query); // use default per-segment timeout
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling QueryContext.getPerSegmentTimeout(long defaultPerSegmentTimeout) when the context contains PER_SEGMENT_TIMEOUT_KEY ("timeoutPerSegmentQuery") with a value < 0, e.g. `{"timeoutPerSegmentQuery": -1000}`.

Common situations: Copy-pasted query context from another engine where -1 meant 'unlimited'; generated query contexts with placeholder negatives; client bugs computing per-segment budgets from a negative remainder.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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