{"id":"28250e5dabcacb45","repo":"go-redis/redis","slug":"ft-aggregate-steps-cannot-be-combined-with-the-de","errorCode":null,"errorMessage":"FT.AGGREGATE: Steps cannot be combined with the deprecated Load, Apply, GroupBy, SortBy and SortByMax fields","messagePattern":"FT\\.AGGREGATE: Steps cannot be combined with the deprecated Load, Apply, GroupBy, SortBy and SortByMax fields","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_commands.go","lineNumber":691,"sourceCode":"\n// FTAggregate - Performs a search query on an index and applies a series of aggregate transformations to the result.\n// The 'index' parameter specifies the index to search, and the 'query' parameter specifies the search query.\n// For more information, please refer to the Redis documentation:\n// [FT.AGGREGATE]: (https://redis.io/commands/ft.aggregate/)\nfunc (c cmdable) FTAggregate(ctx context.Context, index string, query string) *MapStringInterfaceCmd {\n\targs := []interface{}{\"FT.AGGREGATE\", index, query}\n\tcmd := NewMapStringInterfaceCmd(ctx, args...)\n\t_ = c(ctx, cmd)\n\treturn cmd\n}\n\n// validateFTAggregateOptions validates mutually exclusive combinations of\n// FTAggregateOptions fields before any command arguments are constructed.\nfunc validateFTAggregateOptions(options *FTAggregateOptions) error {\n\tif len(options.Steps) > 0 {\n\t\tif options.Load != nil || options.Apply != nil || options.GroupBy != nil ||\n\t\t\toptions.SortBy != nil || options.SortByMax != 0 {\n\t\t\treturn fmt.Errorf(\"FT.AGGREGATE: Steps cannot be combined with the deprecated Load, Apply, GroupBy, SortBy and SortByMax fields\")\n\t\t}\n\t\tif options.LoadAll {\n\t\t\tfor _, step := range options.Steps {\n\t\t\t\tif step.Load != nil {\n\t\t\t\t\treturn fmt.Errorf(\"FT.AGGREGATE: LOADALL and LOAD are mutually exclusive\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif options.LoadAll && options.Load != nil {\n\t\treturn fmt.Errorf(\"FT.AGGREGATE: LOADALL and LOAD are mutually exclusive\")\n\t}\n\treturn nil\n}\n\n// appendFTAggregateStep appends the Redis command arguments for a single\n// aggregation pipeline step. Each step must set exactly one of Load, Apply,\n// GroupBy or SortBy.","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_commands.go#L673-L709","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Audit the FTAggregateOptions literal at the call site to ensure only one pipeline representation is populated.","If migrating incrementally, zero out the deprecated fields in the same change that adds Steps."],"exampleFix":"// before\nopts := &FTAggregateOptions{\n    Steps:  []FTAggregateStep{{GroupBy: &FTAggregateGroupBy{Fields: []string{\"cat\"}}}},\n    SortBy: []FTAggregateSortBy{{FieldName: \"price\", Desc: true}}, // deprecated, conflicts\n}\n// after\nopts := &FTAggregateOptions{\n    Steps: []FTAggregateStep{\n        {GroupBy: &FTAggregateGroupBy{Fields: []string{\"cat\"}}},\n        {SortBy: &FTAggregateSortStep{Fields: []FTAggregateSortBy{{FieldName: \"price\", Desc: true}}}},\n    },\n}","handlingStrategy":"validation","validationCode":"func validateAggregateOptionsMixed(opts *FTAggregateOptions) error {\n\tif len(opts.Steps) == 0 { return nil }\n\tif opts.Load != nil || opts.Apply != nil || opts.GroupBy != nil || opts.SortBy != nil || opts.SortByMax != 0 {\n\t\treturn fmt.Errorf(\"do not mix Steps with deprecated Load/Apply/GroupBy/SortBy/SortByMax\")\n\t}\n\treturn nil\n}","typeGuard":"func usesStepsOnly(opts *FTAggregateOptions) bool {\n\treturn len(opts.Steps) > 0 &&\n\t\topts.Load == nil && opts.Apply == nil && opts.GroupBy == nil &&\n\t\topts.SortBy == nil && opts.SortByMax == 0\n}","tryCatchPattern":null,"preventionTips":["Commit to one FT.AGGREGATE API per codebase module: Steps or the deprecated flat fields, not both.","When migrating, clear the deprecated fields in the same change that introduces Steps.","Add a unit test asserting the options struct your app builds satisfies usesStepsOnly (or its deprecated equivalent)."],"tags":["ft-aggregate","validation","api-migration","api-misuse"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}