{"record":{"id":"703bf1a4de229e4b","repo":"redis/go-redis","slug":"ft-aggregate-sortbymax-must-follow-a-sortby-step","errorCode":null,"errorMessage":"FT.AGGREGATE: SortByMax must follow a SortBy step","messagePattern":"FT\\.AGGREGATE: SortByMax must follow a SortBy step","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_builders.go","lineNumber":366,"sourceCode":"// regardless of position in the pipeline.\nfunc (b *AggregateBuilder) SortBy(field string, asc bool) *AggregateBuilder {\n\tsb := FTAggregateSortBy{FieldName: field, Asc: asc, Desc: !asc}\n\tif n := len(b.options.Steps); n > 0 && b.options.Steps[n-1].SortBy != nil {\n\t\tb.options.Steps[n-1].SortBy.Fields = append(b.options.Steps[n-1].SortBy.Fields, sb)\n\t\treturn b\n\t}\n\tb.options.Steps = append(b.options.Steps, FTAggregateStep{\n\t\tSortBy: &FTAggregateSortByStep{Fields: []FTAggregateSortBy{sb}},\n\t})\n\treturn b\n}\n\n// SortByMax sets MAX <n> on the last SORTBY step. The last step must be a\n// SORTBY; otherwise Run will return an error.\nfunc (b *AggregateBuilder) SortByMax(max int) *AggregateBuilder {\n\tn := len(b.options.Steps)\n\tif n == 0 || b.options.Steps[n-1].SortBy == nil {\n\t\tb.setErr(fmt.Errorf(\"FT.AGGREGATE: SortByMax must follow a SortBy step\"))\n\t\treturn b\n\t}\n\tb.options.Steps[n-1].SortBy.Max = max\n\treturn b\n}\n\n// Filter sets FILTER <expr>.\nfunc (b *AggregateBuilder) Filter(expr string) *AggregateBuilder {\n\tb.options.Filter = expr\n\treturn b\n}\n\n// WithCursor enables WITHCURSOR [COUNT <n>] [MAXIDLE <ms>].\nfunc (b *AggregateBuilder) WithCursor(count, maxIdle int) *AggregateBuilder {\n\tb.options.WithCursor = true\n\tif b.options.WithCursorOptions == nil {\n\t\tb.options.WithCursorOptions = &FTAggregateWithCursor{}\n\t}","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/search_builders.go#L348-L384","documentation":"FT.AGGREGATE's SortByMax must be applied to a SORTBY step that was already added via SortBy or Steps. The AggregateBuilder checks that the last step is a SORTBY step before setting MAX on it; otherwise it records this error on the builder and Run will fail. This is a builder-ordering contract, not a server error.","triggerScenarios":"Calling b.SortByMax(n) before calling b.SortBy(...), or calling it after any non-SortBy step (e.g. after Apply, GroupBy, Filter) was appended, so b.options.Steps is empty or the last step has no SortBy.","commonSituations":"Refactoring from the deprecated SortByMax field on FTAggregateOptions to the step-based builder and calling SortByMax out of order; chaining builder methods in the wrong sequence.","solutions":["Call SortBy (or add a SortBy step) immediately before SortByMax so the last step is SORTBY","Remove the SortByMax call if you never intend to sort","Use the new Steps API with a single SortBy step carrying the Max field instead of SortByMax"],"exampleFix":"// before\nb := client.FTAggregateWithArgs(idx, q).\n\tGroupBy(...).\n\tSortByMax(5)\n// after\nb := client.FTAggregateWithArgs(idx, q).\n\tGroupBy(...).\n\tSortBy(&redis.FTAggregateSortBy{FieldName: \"@score\"}).\n\tSortByMax(5)","handlingStrategy":"validation","validationCode":"steps := len(b.options.Steps)\nif steps == 0 || b.options.Steps[steps-1].SortBy == nil {\n    return fmt.Errorf(\"SortByMax requires a preceding SortBy step\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always place SortByMax immediately after a SortBy call in builder chains","Prefer the Steps API where Max is a field of the SortBy step","Check builder.Err() before calling Run"],"tags":["search","ft-aggregate","builder-order","validation"],"backgroundTag":"ft-aggregate-sortby-max-order","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}