{"record":{"id":"172e74b189d7104c","repo":"redis/jedis","slug":"aggregationbuilder-must-have-cursor-configured","errorCode":null,"errorMessage":"AggregationBuilder must have cursor configured","messagePattern":"AggregationBuilder must have cursor configured","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/search/aggr/AggregateIterator.java","lineNumber":83,"sourceCode":"  private final String indexName;\n  private final Integer batchSize;\n\n  // Connection pool entry - can be either Connection or Pool<Connection>\n  private final Map.Entry<?, ?> connectionEntry;\n  private Long cursorId = -1L;\n  private AggregationResult aggrCommandResult;\n\n  /**\n   * Creates a new AggregateIterator.\n   * @param connectionProvider the connection provider for cluster/standalone Redis\n   * @param indexName the search index name\n   * @param aggregationBuilder the aggregation query with cursor configuration\n   * @throws IllegalArgumentException if aggregation doesn't have cursor configured\n   */\n  public AggregateIterator(ConnectionProvider connectionProvider, String indexName,\n      AggregationBuilder aggregationBuilder) {\n    if (!aggregationBuilder.isWithCursor()) {\n      throw new IllegalArgumentException(\"AggregationBuilder must have cursor configured\");\n    }\n\n    this.indexName = indexName;\n    this.batchSize = aggregationBuilder.getCursorCount();\n\n    // Get connection pool entry - use getPrimaryNodesConnectionMap() to get pool-based connections\n    Map<?, ?> connectionMap = connectionProvider.getPrimaryNodesConnectionMap();\n    if (connectionMap.isEmpty()) {\n      throw new JedisException(\"No connections available from connection provider\");\n    }\n    // Randomly select an entry from the map to distribute load across shards\n    List<? extends Map.Entry<?, ?>> entries = new ArrayList<>(connectionMap.entrySet());\n    this.connectionEntry = entries.get(ThreadLocalRandom.current().nextInt(entries.size()));\n\n    // Execute initial aggregation command\n    initializeAggregation(aggregationBuilder);\n  }\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/search/aggr/AggregateIterator.java#L65-L101","documentation":"AggregateIterator iterates the results of a cursor-based (WITHCURSOR) FT.AGGREGATE query, fetching batches via CURSOR. The constructor throws IllegalArgumentException if the supplied AggregationBuilder lacks a cursor, because streaming iteration is impossible without one.","triggerScenarios":"Constructing new AggregateIterator(provider, index, aggregationBuilder) with a builder where withCursor()/cursor(...) was never called; calling an iterator-returning API while forgetting to enable cursoring on the aggregation.","commonSituations":"Building aggregations dynamically and conditionally enabling cursors; copying sample code without the .cursor(...) call; large-result iteration APIs invoked with plain aggregation builders.","solutions":["Enable cursoring before iterating: builder.cursor(...) (or withCursor()) on the AggregationBuilder","Use the non-iterator aggregate() API if you do not need cursor-based iteration","Check builder.isWithCursor() before constructing AggregateIterator"],"exampleFix":"// before\nAggregationBuilder ab = new AggregationBuilder(\"*\").groupBy(\"genre\");\nAggregateIterator it = new AggregateIterator(client, \"idx\", ab); // throws\n// after\nAggregationBuilder ab = new AggregationBuilder(\"*\").groupBy(\"genre\").cursor(100);\nAggregateIterator it = new AggregateIterator(client, \"idx\", ab);","handlingStrategy":"validation","validationCode":"if (!aggregationBuilder.isWithCursor()) {\n  aggregationBuilder.cursor(100); // or use aggregate() instead of the iterator\n}\nAggregateIterator it = new AggregateIterator(provider, indexName, aggregationBuilder);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call .cursor(batchSize) on aggregation builders used with AggregateIterator","Prefer the iterator-returning API only when streaming large results; otherwise use aggregate()","Centralize aggregation construction so cursoring is applied in one place"],"tags":["redisearch","aggregation","cursor","precondition"],"backgroundTag":"missing-required-argument","analyzedSha":"6dac31d4c224fb3257c216f3985340c6f500cdcb","analyzedAt":"2026-09-08T04:55:01.204Z","contentChangedAt":"2026-09-08T04:55:01.204Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}