apache/druid · error · BadQueryContextException
Configured maxScatterGatherBytes =
Error message
Configured maxScatterGatherBytes = %d is more than enforced limit of %d.
What it means
Druid enforces an upper bound on `maxScatterGatherBytes` — the maximum number of bytes a query may buffer while gathering results from data nodes. QueryContext.verifyMaxScatterGatherBytes throws BadQueryContextException when the query's context value exceeds the enforced server limit. This protects brokers from unbounded memory use during fan-out query execution.
Solutions
- Reduce the query's `maxScatterGatherBytes` to a value at or below the enforced limit shown in the message.
- If the workload legitimately needs more memory, raise the server-side enforced limit (broker's maxScatterGatherBytes config) and restart/roll out.
- Restructure the query to return fewer bytes (filters, limits, approximate sketches like DataSketches theta/HLL) instead of raising limits.
- Catch BadQueryContextException and surface the enforced limit to the user so they can shrink the query or contact the operator.
Example fix
// before
context.put("maxScatterGatherBytes", 10L * 1024 * 1024 * 1024); // exceeds 1 GiB limit
// after
long limit = 1024L * 1024 * 1024; // enforced by broker
context.put("maxScatterGatherBytes", Math.min(10L * 1024 * 1024 * 1024, limit)); Defensive patterns
Strategy: validation
Validate before calling
Object msg = query.getContext().get("maxScatterGatherBytes");
long enforced = /* broker config */ 1024L * 1024 * 1024;
if (msg instanceof Number && ((Number) msg).longValue() > enforced) {
query.getContext().put("maxScatterGatherBytes", enforced);
} Type guard
boolean withinScatterGatherLimit(Object v, long limit) {
return !(v instanceof Number) || ((Number) v).longValue() <= limit;
} Try / catch
try {
client.query(query);
} catch (BadQueryContextException e) {
if (e.getMessage().contains("maxScatterGatherBytes")) {
long limit = parseEnforcedLimit(e.getMessage());
query.getContext().put("maxScatterGatherBytes", limit);
client.query(query);
} else {
throw e;
}
} Prevention
- Only raise maxScatterGatherBytes with explicit operator approval; it maps to broker memory.
- Reduce result size (filters, LIMIT, sketches) before raising byte limits.
- Keep client-side caps in sync with broker configuration.
- Alert on queries rejected for byte-limit breaches — they indicate oversized query patterns.
When it happens
Trigger: Calling QueryContext.verifyMaxScatterGatherBytes(long maxScatterGatherBytesLimit) (invoked via withMaxScatterGatherBytes) when the context's MAX_SCATTER_GATHER_BYTES_KEY ("maxScatterGatherBytes") value exceeds the enforced limit, e.g. query asks for 10 GiB while the broker enforces 1 GiB.
Common situations: Clients inflating maxScatterGatherBytes to run very large group-bys/top-N queries on a broker whose druid.server.http.maxScatterGatherBytes or maxQueryTimeout-style cap was configured lower; operators tightening limits after capacity incidents; large-result BI tools with hard-coded generous limits.
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
- Query[ ] url[ ] total bytes gathered[%,d] exceeds…
- At most one of 'druid.broker.segment.watchedTiers' and…
- BroadcastTablesTooLarge
- Cannot set both [realtimeSegmentsMode] and deprecated…
- Configured timeout =
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/1c70768231d1f232.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/QueryContext.java:586
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(
StringUtils.format(
"Configured %s = %d is more than enforced limit of %d.",
QueryContexts.MAX_SCATTER_GATHER_BYTES_KEY,
curr,
maxScatterGatherBytesLimit
)
);
}
}
public int getNumRetriesOnMissingSegments(int defaultValue)
{
return getInt(QueryContexts.NUM_RETRIES_ON_MISSING_SEGMENTS_KEY, defaultValue);
}
public boolean allowReturnPartialResults(boolean defaultValue)
{
return getBoolean(QueryContexts.RETURN_PARTIAL_RESULTS_KEY, defaultValue);View on GitHub (pinned to 9b90983fd2)