{"record":{"id":"4914d228cfe66e39","repo":"apache/druid","slug":"timeout-timeout-must-be-a-non-negative-value-bu","errorCode":null,"errorMessage":"Timeout [timeout] must be a non negative value, but was %d","messagePattern":"Timeout \\[timeout\\] must be a non negative value, but was (.+?)","errorType":"validation","errorClass":"BadQueryContextException","httpStatus":400,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/QueryContext.java","lineNumber":508,"sourceCode":"  }\n\n  public boolean hasTimeout()\n  {\n    return getTimeout() != QueryContexts.NO_TIMEOUT;\n  }\n\n  public long getTimeout()\n  {\n    return getTimeout(getDefaultTimeout());\n  }\n\n  public long getTimeout(long defaultTimeout)\n  {\n    final long timeout = getLong(QueryContexts.TIMEOUT_KEY, defaultTimeout);\n    if (timeout >= 0) {\n      return timeout;\n    }\n    throw new BadQueryContextException(\n        StringUtils.format(\n            \"Timeout [%s] must be a non negative value, but was %d\",\n            QueryContexts.TIMEOUT_KEY,\n            timeout\n        )\n    );\n  }\n\n  @Nullable\n  public Duration getTimeoutDuration()\n  {\n    if (hasTimeout()) {\n      return Duration.ofMillis(getTimeout());\n    }\n    return null;\n  }\n\n  public long getDefaultTimeout()","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/QueryContext.java#L490-L526","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the query's `context` object and set `timeout` to a non-negative millisecond value, or remove the key to use the default timeout.","If the timeout is computed at runtime, clamp it: `long t = Math.max(0, deadline - now);`","Check the calling client/library for integer underflow or sign errors when deriving the timeout.","Catch BadQueryContextException in the query-submitting code and surface a clear client-side validation message before sending the query."],"exampleFix":"// before\ncontext.put(\"timeout\", deadlineMillis - System.currentTimeMillis()); // can be negative\n// after\nlong timeout = Math.max(0, deadlineMillis - System.currentTimeMillis());\ncontext.put(\"timeout\", timeout);","handlingStrategy":"validation","validationCode":"Object timeout = query.getContext().get(\"timeout\");\nif (timeout instanceof Number && ((Number) timeout).longValue() < 0) {\n    throw new IllegalArgumentException(\"timeout must be non-negative, got \" + timeout);\n}","typeGuard":"boolean isValidTimeout(Object v) {\n    return !(v instanceof Number) || ((Number) v).longValue() >= 0;\n}","tryCatchPattern":"try {\n    client.query(query);\n} catch (DruidException | BadQueryContextException e) {\n    if (e.getMessage().contains(\"must be a non negative value\")) {\n        query.getContext().remove(\"timeout\"); // fall back to default\n        client.query(query);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Never put negative sentinel values (e.g. -1) in query context; omit the key for defaults.","Clamp computed timeouts with Math.max(0, value) before submission.","Validate query context in your query-builder layer before sending.","Unit-test query builders with edge-case deadline arithmetic to catch underflow."],"tags":["query-context","timeout","validation","druid"],"backgroundTag":"value-out-of-range","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}