redis/jedis · error · IllegalArgumentException

AggregationBuilder must have cursor configured

Error message

AggregationBuilder must have cursor configured

What it means

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.

Solutions

  1. Enable cursoring before iterating: builder.cursor(...) (or withCursor()) on the AggregationBuilder
  2. Use the non-iterator aggregate() API if you do not need cursor-based iteration
  3. Check builder.isWithCursor() before constructing AggregateIterator

Example fix

// before
AggregationBuilder ab = new AggregationBuilder("*").groupBy("genre");
AggregateIterator it = new AggregateIterator(client, "idx", ab); // throws
// after
AggregationBuilder ab = new AggregationBuilder("*").groupBy("genre").cursor(100);
AggregateIterator it = new AggregateIterator(client, "idx", ab);
Defensive patterns

Strategy: validation

Validate before calling

if (!aggregationBuilder.isWithCursor()) {
  aggregationBuilder.cursor(100); // or use aggregate() instead of the iterator
}
AggregateIterator it = new AggregateIterator(provider, indexName, aggregationBuilder);

Prevention

When it happens

Trigger: 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.

Common situations: Building aggregations dynamically and conditionally enabling cursors; copying sample code without the .cursor(...) call; large-result iteration APIs invoked with plain aggregation builders.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08). Data as JSON: /api/errors/172e74b189d7104c. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/redis/clients/jedis/search/aggr/AggregateIterator.java:83

  private final String indexName;
  private final Integer batchSize;

  // Connection pool entry - can be either Connection or Pool<Connection>
  private final Map.Entry<?, ?> connectionEntry;
  private Long cursorId = -1L;
  private AggregationResult aggrCommandResult;

  /**
   * Creates a new AggregateIterator.
   * @param connectionProvider the connection provider for cluster/standalone Redis
   * @param indexName the search index name
   * @param aggregationBuilder the aggregation query with cursor configuration
   * @throws IllegalArgumentException if aggregation doesn't have cursor configured
   */
  public AggregateIterator(ConnectionProvider connectionProvider, String indexName,
      AggregationBuilder aggregationBuilder) {
    if (!aggregationBuilder.isWithCursor()) {
      throw new IllegalArgumentException("AggregationBuilder must have cursor configured");
    }

    this.indexName = indexName;
    this.batchSize = aggregationBuilder.getCursorCount();

    // Get connection pool entry - use getPrimaryNodesConnectionMap() to get pool-based connections
    Map<?, ?> connectionMap = connectionProvider.getPrimaryNodesConnectionMap();
    if (connectionMap.isEmpty()) {
      throw new JedisException("No connections available from connection provider");
    }
    // Randomly select an entry from the map to distribute load across shards
    List<? extends Map.Entry<?, ?>> entries = new ArrayList<>(connectionMap.entrySet());
    this.connectionEntry = entries.get(ThreadLocalRandom.current().nextInt(entries.size()));

    // Execute initial aggregation command
    initializeAggregation(aggregationBuilder);
  }

View on GitHub (pinned to 6dac31d4c2)