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
- Set `timeoutPerSegmentQuery` in the query context to a non-negative millisecond value, or remove the key to use the default.
- Clamp any computed value: `long t = Math.max(0, computed);` before putting it in the context.
- Search the query-issuing code for negative sentinel values and replace them with omission of the key.
- 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
- Omit timeoutPerSegmentQuery rather than sending negative placeholders.
- Sanitize any context value copied from other query engines.
- Clamp computed per-segment budgets with Math.max(0, value).
- Keep a shared, validated context-builder utility for all query clients.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Timeout [timeout] must be a non negative value, but was %d
- Timeout [maxDefaultTimeout] must be a non negative value, bu
- Query [%s] timed out
- Expected key [%s] to be referring to one of the values [%s]
- Expected key [%s] to be of type [%s], but got [%s]
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/122d4d150ae1fcc7.
Report an issue: GitHub.