apache/druid · error · BadQueryContextException

Timeout [timeout] must be a non negative value, but was %d

Error message

Timeout [timeout] must be a non negative value, but was %d

What it means

Druid validates the `timeout` query-context key before using it. If the caller supplies a negative long for the timeout (in milliseconds), QueryContext.getTimeout refuses to return it and throws BadQueryContextException. This guard exists because a negative timeout is meaningless and would corrupt the query deadline computation in QueryContexts/ChainedExecutionQueryRunner.

Source

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

  }

  public boolean hasTimeout()
  {
    return getTimeout() != QueryContexts.NO_TIMEOUT;
  }

  public long getTimeout()
  {
    return getTimeout(getDefaultTimeout());
  }

  public long getTimeout(long defaultTimeout)
  {
    final long timeout = getLong(QueryContexts.TIMEOUT_KEY, defaultTimeout);
    if (timeout >= 0) {
      return timeout;
    }
    throw new BadQueryContextException(
        StringUtils.format(
            "Timeout [%s] must be a non negative value, but was %d",
            QueryContexts.TIMEOUT_KEY,
            timeout
        )
    );
  }

  @Nullable
  public Duration getTimeoutDuration()
  {
    if (hasTimeout()) {
      return Duration.ofMillis(getTimeout());
    }
    return null;
  }

  public long getDefaultTimeout()

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Inspect the query's `context` object and set `timeout` to a non-negative millisecond value, or remove the key to use the default timeout.
  2. If the timeout is computed at runtime, clamp it: `long t = Math.max(0, deadline - now);`
  3. Check the calling client/library for integer underflow or sign errors when deriving the timeout.
  4. Catch BadQueryContextException in the query-submitting code and surface a clear client-side validation message before sending the query.

Example fix

// before
context.put("timeout", deadlineMillis - System.currentTimeMillis()); // can be negative
// after
long timeout = Math.max(0, deadlineMillis - System.currentTimeMillis());
context.put("timeout", timeout);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try {
    client.query(query);
} catch (DruidException | BadQueryContextException e) {
    if (e.getMessage().contains("must be a non negative value")) {
        query.getContext().remove("timeout"); // fall back to default
        client.query(query);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling QueryContext.getTimeout(long defaultTimeout) (directly or via timeout()) when the query context map contains TIMEOUT_KEY ("timeout") with a value < 0, e.g. `{"timeout": -5000}` supplied by a client in the query request's context object.

Common situations: Client SDKs or dashboard tools computing a remaining budget as an int that underflowed or was computed from a past deadline; hand-written JSON query payloads with negative timeouts; programmatic query builders subtracting timestamps in the wrong order.

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/4914d228cfe66e39. Report an issue: GitHub.