go-redis/redis · error

FT.AGGREGATE: Steps cannot be combined with the deprecated L

Error message

FT.AGGREGATE: Steps cannot be combined with the deprecated Load, Apply, GroupBy, SortBy and SortByMax fields

What it means

Returned by validateFTAggregateOptions (via FTAggregateQuery) when FTAggregateOptions.Steps is non-empty AND any of the deprecated top-level fields (Load, Apply, GroupBy, SortBy, SortByMax) is also set. The library exposes two FT.AGGREGATE APIs: the newer Steps-based pipeline and the older flat Load/Apply/GroupBy/SortBy fields; they cannot be mixed in one call because they would serialize conflicting pipeline stages.

Source

Thrown at search_commands.go:691

// FTAggregate - Performs a search query on an index and applies a series of aggregate transformations to the result.
// The 'index' parameter specifies the index to search, and the 'query' parameter specifies the search query.
// For more information, please refer to the Redis documentation:
// [FT.AGGREGATE]: (https://redis.io/commands/ft.aggregate/)
func (c cmdable) FTAggregate(ctx context.Context, index string, query string) *MapStringInterfaceCmd {
	args := []interface{}{"FT.AGGREGATE", index, query}
	cmd := NewMapStringInterfaceCmd(ctx, args...)
	_ = c(ctx, cmd)
	return cmd
}

// validateFTAggregateOptions validates mutually exclusive combinations of
// FTAggregateOptions fields before any command arguments are constructed.
func validateFTAggregateOptions(options *FTAggregateOptions) error {
	if len(options.Steps) > 0 {
		if options.Load != nil || options.Apply != nil || options.GroupBy != nil ||
			options.SortBy != nil || options.SortByMax != 0 {
			return fmt.Errorf("FT.AGGREGATE: Steps cannot be combined with the deprecated Load, Apply, GroupBy, SortBy and SortByMax fields")
		}
		if options.LoadAll {
			for _, step := range options.Steps {
				if step.Load != nil {
					return fmt.Errorf("FT.AGGREGATE: LOADALL and LOAD are mutually exclusive")
				}
			}
		}
	}
	if options.LoadAll && options.Load != nil {
		return fmt.Errorf("FT.AGGREGATE: LOADALL and LOAD are mutually exclusive")
	}
	return nil
}

// appendFTAggregateStep appends the Redis command arguments for a single
// aggregation pipeline step. Each step must set exactly one of Load, Apply,
// GroupBy or SortBy.

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Pick one API: move all pipeline stages into Steps and clear Load/Apply/GroupBy/SortBy/SortByMax, OR remove Steps and use the deprecated fields exclusively.
  2. Audit the FTAggregateOptions literal at the call site to ensure only one pipeline representation is populated.
  3. If migrating incrementally, zero out the deprecated fields in the same change that adds Steps.

Example fix

// before
opts := &FTAggregateOptions{
    Steps:  []FTAggregateStep{{GroupBy: &FTAggregateGroupBy{Fields: []string{"cat"}}}},
    SortBy: []FTAggregateSortBy{{FieldName: "price", Desc: true}}, // deprecated, conflicts
}
// after
opts := &FTAggregateOptions{
    Steps: []FTAggregateStep{
        {GroupBy: &FTAggregateGroupBy{Fields: []string{"cat"}}},
        {SortBy: &FTAggregateSortStep{Fields: []FTAggregateSortBy{{FieldName: "price", Desc: true}}}},
    },
}
Defensive patterns

Strategy: validation

Validate before calling

func validateAggregateOptionsMixed(opts *FTAggregateOptions) error {
	if len(opts.Steps) == 0 { return nil }
	if opts.Load != nil || opts.Apply != nil || opts.GroupBy != nil || opts.SortBy != nil || opts.SortByMax != 0 {
		return fmt.Errorf("do not mix Steps with deprecated Load/Apply/GroupBy/SortBy/SortByMax")
	}
	return nil
}

Type guard

func usesStepsOnly(opts *FTAggregateOptions) bool {
	return len(opts.Steps) > 0 &&
		opts.Load == nil && opts.Apply == nil && opts.GroupBy == nil &&
		opts.SortBy == nil && opts.SortByMax == 0
}

Prevention

When it happens

Trigger: Building FTAggregateOptions with both Steps populated and any of Load/Apply/GroupBy/SortBy/SortByMax non-zero. Triggered in FTAggregateQuery (and the AggregateCmd builder) before any Redis argument is emitted.

Common situations: Partially migrating from the deprecated flat API to Steps and leaving a stale SortBy or GroupBy set. Copy-pasting an options struct that already had GroupBy and appending a Steps entry. Default struct initialization populating both paths.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/28250e5dabcacb45.json. Report an issue: GitHub.