{"record":{"id":"cfedde221a87cbf0","repo":"apache/druid","slug":"ambiguous-build-limitspec-s-already-set","errorCode":null,"errorMessage":"Ambiguous build, limitSpec[%s] already set","messagePattern":"Ambiguous build, limitSpec\\[(.+?)\\] already set","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java","lineNumber":1125,"sourceCode":"      ensureExplicitLimitSpecNotSet();\n      this.orderByColumnSpecs = new ArrayList<>(columnSpec);\n      this.postProcessingFn = null;\n      return this;\n    }\n\n    public Builder setLimitSpec(LimitSpec limitSpec)\n    {\n      Preconditions.checkNotNull(limitSpec);\n      ensureFluentLimitsNotSet();\n      this.limitSpec = limitSpec;\n      this.postProcessingFn = null;\n      return this;\n    }\n\n    private void ensureExplicitLimitSpecNotSet()\n    {\n      if (limitSpec != null) {\n        throw new ISE(\"Ambiguous build, limitSpec[%s] already set\", limitSpec);\n      }\n    }\n\n    private void ensureFluentLimitsNotSet()\n    {\n      if (!(limit == Integer.MAX_VALUE && orderByColumnSpecs.isEmpty())) {\n        throw new ISE(\"Ambiguous build, limit[%s] or columnSpecs[%s] already set.\", limit, orderByColumnSpecs);\n      }\n    }\n\n    public Builder setQuerySegmentSpec(QuerySegmentSpec querySegmentSpec)\n    {\n      this.querySegmentSpec = querySegmentSpec;\n      return this;\n    }\n\n    public Builder setDimFilter(@Nullable DimFilter dimFilter)\n    {","sourceCodeStart":1107,"sourceCodeEnd":1143,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/groupby/GroupByQuery.java#L1107-L1143","documentation":"GroupByQuery.Builder supports two ways to specify limits: setLimitSpec(...) and the fluent setLimit()/addOrderByColumnSpec() API. Using both is ambiguous, so ensureExplicitLimitSpecNotSet throws ISE when an explicit limitSpec is already set and fluent limit methods are then used.","triggerScenarios":"Calling Builder.setLimitSpec(...) (or limitSpec field set) and subsequently calling setLimit/addOrderByColumnSpec/limit-fluent methods on the same builder.","commonSituations":"Query-builder code mixing legacy limitSpec JSON with newer fluent API calls; deserialization followed by programmatic limit modification; refactor leftovers.","solutions":["Pick one API: use only setLimitSpec OR only the fluent setLimit/addOrderByColumnSpec methods","Before fluent calls, guard with hasFluentization-like checks or clear the existing limitSpec","Remove the redundant setLimitSpec call when fluent limits already encode the desired limit/sort"],"exampleFix":"// before\nbuilder.setLimitSpec(limitSpec).setLimit(10);\n// after\nbuilder.setLimitSpec(limitSpec); // or: builder.setLimit(10).addOrderByColumnSpec(...);","handlingStrategy":"validation","validationCode":"if (builder != null && explicitLimitSpecSet && fluentLimitUsed) throw new IllegalStateException(\"Use either setLimitSpec or fluent limit APIs, not both\");","typeGuard":"null","tryCatchPattern":"try { builder.build(); } catch (IllegalStateException e) { if (e.getMessage().startsWith(\"Ambiguous build\")) { /* choose one limit mechanism and rebuild */ } throw e; }","preventionTips":["Standardize on one limit API (prefer setLimitSpec) across your query-building code","Do not mutate builders after passing limitSpec through constructors/factories"],"tags":["groupby","builder","ambiguous-build"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}