{"id":"abe8e1d6c2ca8dea","repo":"go-redis/redis","slug":"ft-aggregate-each-step-must-set-exactly-one-of-lo","errorCode":null,"errorMessage":"FT.AGGREGATE: each step must set exactly one of Load, Apply, GroupBy, SortBy (got %d)","messagePattern":"FT\\.AGGREGATE: each step must set exactly one of Load, Apply, GroupBy, SortBy \\(got (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_commands.go","lineNumber":725,"sourceCode":"// 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.\nfunc appendFTAggregateStep(args []interface{}, step FTAggregateStep) ([]interface{}, error) {\n\tset := 0\n\tif step.Load != nil {\n\t\tset++\n\t}\n\tif step.Apply != nil {\n\t\tset++\n\t}\n\tif step.GroupBy != nil {\n\t\tset++\n\t}\n\tif step.SortBy != nil {\n\t\tset++\n\t}\n\tif set != 1 {\n\t\treturn args, fmt.Errorf(\"FT.AGGREGATE: each step must set exactly one of Load, Apply, GroupBy, SortBy (got %d)\", set)\n\t}\n\n\tswitch {\n\tcase step.Load != nil:\n\t\targs = append(args, \"LOAD\")\n\t\tcountIdx := len(args)\n\t\targs = append(args, 0)\n\t\tcount := 0\n\t\targs = append(args, step.Load.Field)\n\t\tcount++\n\t\tif step.Load.As != \"\" {\n\t\t\targs = append(args, \"AS\", step.Load.As)\n\t\t\tcount += 2\n\t\t}\n\t\targs[countIdx] = count\n\tcase step.Apply != nil:\n\t\targs = append(args, \"APPLY\", step.Apply.Field)\n\t\tif step.Apply.As != \"\" {","sourceCodeStart":707,"sourceCodeEnd":743,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_commands.go#L707-L743","documentation":"Returned by appendFTAggregateStep when a single FTAggregateStep does not set exactly one of Load, Apply, GroupBy, or SortBy (the count is reported in '%d'). Each Step must map to exactly one Redis pipeline stage, so zero or multiple set fields is ambiguous and rejected before serialization.","triggerScenarios":"Adding an FTAggregateStep with none of Load/Apply/GroupBy/SortBy set (count 0), or with two or more set (count >= 2). Triggered while iterating options.Steps in FTAggregateQuery.","commonSituations":"Empty step literal {{}} left as a placeholder. Reusing a step struct and setting Apply on top of an already-set GroupBy. Conditional logic that sometimes leaves all fields nil. Misunderstanding that each Step holds exactly one stage.","solutions":["Populate exactly one of Load, Apply, GroupBy, or SortBy on each FTAggregateStep.","Split a step that needs multiple stages into multiple Steps (the pipeline is ordered).","Remove placeholder/empty entries from the Steps slice before calling FTAggregate.","Add a unit assertion that each step sets exactly one field to catch regressions."],"exampleFix":"// before\nSteps: []FTAggregateStep{\n    {},                                              // count 0 -> error\n    {Apply: &FTAggregateApply{Field: \"@price/2\"},\n     GroupBy: &FTAggregateGroupBy{Fields: []string{\"cat\"}}}, // count 2 -> error\n}\n// after\nSteps: []FTAggregateStep{\n    {GroupBy: &FTAggregateGroupBy{Fields: []string{\"cat\"}}},\n    {Apply: &FTAggregateApply{Field: \"@price/2\", As: \"half\"}},\n}","handlingStrategy":"validation","validationCode":"func validateStepExactlyOne(step FTAggregateStep) error {\n\tset := 0\n\tif step.Load != nil { set++ }\n\tif step.Apply != nil { set++ }\n\tif step.GroupBy != nil { set++ }\n\tif step.SortBy != nil { set++ }\n\tif set != 1 {\n\t\treturn fmt.Errorf(\"step must set exactly one of Load/Apply/GroupBy/SortBy, got %d\", set)\n\t}\n\treturn nil\n}","typeGuard":"func stepIsWellFormed(step FTAggregateStep) bool {\n\tset := 0\n\tif step.Load != nil { set++ }\n\tif step.Apply != nil { set++ }\n\tif step.GroupBy != nil { set++ }\n\tif step.SortBy != nil { set++ }\n\treturn set == 1\n}","tryCatchPattern":null,"preventionTips":["Model each pipeline stage as its own FTAggregateStep; never bundle two stages in one step.","Filter placeholder/empty steps out of the Steps slice before calling FTAggregate.","Add a builder/factory that constructs steps so callers cannot leave a zero-valued step."],"tags":["ft-aggregate","validation","steps","api-misuse"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}