go-redis/redis · error
FT.AGGREGATE: ASC and DESC are mutually exclusive
Error message
FT.AGGREGATE: ASC and DESC are mutually exclusive
What it means
Returned by appendFTAggregateStep (the Steps path) when a SortBy field inside an FTAggregateStep.SortBy.Fields entry has both Asc and Desc set to true. A SORTBY field can carry one direction; both is contradictory, so the builder refuses to serialize the step.
Source
Thrown at search_commands.go:766
args = append(args, step.GroupBy.Fields...)
for _, reducer := range step.GroupBy.Reduce {
args = append(args, "REDUCE", reducer.Reducer.String())
if reducer.Args != nil {
args = append(args, len(reducer.Args))
args = append(args, reducer.Args...)
} else {
args = append(args, 0)
}
if reducer.As != "" {
args = append(args, "AS", reducer.As)
}
}
case step.SortBy != nil:
args = append(args, "SORTBY")
sortByOptions := []interface{}{}
for _, sortBy := range step.SortBy.Fields {
if sortBy.Asc && sortBy.Desc {
return args, fmt.Errorf("FT.AGGREGATE: ASC and DESC are mutually exclusive")
}
sortByOptions = append(sortByOptions, sortBy.FieldName)
if sortBy.Asc {
sortByOptions = append(sortByOptions, "ASC")
}
if sortBy.Desc {
sortByOptions = append(sortByOptions, "DESC")
}
}
args = append(args, len(sortByOptions))
args = append(args, sortByOptions...)
if step.SortBy.Max > 0 {
args = append(args, "MAX", step.SortBy.Max)
}
}
return args, nil
}
View on GitHub (pinned to 36d97525cd)
Solutions
- Set at most one of Asc or Desc per FTAggregateSortBy (leave both false for server-default ASC).
- Audit the construction of step.SortBy.Fields to enforce mutual exclusion.
- Map a single direction enum to Asc xor Desc before building the step.
Example fix
// before
{SortBy: &FTAggregateSortStep{Fields: []FTAggregateSortBy{{FieldName: "price", Asc: true, Desc: true}}}}
// after
{SortBy: &FTAggregateSortStep{Fields: []FTAggregateSortBy{{FieldName: "price", Desc: true}}}} Defensive patterns
Strategy: validation
Validate before calling
func validateStepSortDirections(s *FTAggregateSortStep) error {
for i, f := range s.Fields {
if f.Asc && f.Desc {
return fmt.Errorf("SortBy.Fields[%d]: Asc and Desc both set", i)
}
}
return nil
} Type guard
func directionUnique(f FTAggregateSortBy) bool { return !(f.Asc && f.Desc) } Prevention
- Represent direction as a single enum and map to Asc xor Desc.
- When toggling one direction, clear the other.
- Never default both flags to true.
When it happens
Trigger: FTAggregateStep{SortBy: &FTAggregateSortStep{Fields: []FTAggregateSortBy{{FieldName: "price", Asc: true, Desc: true}}}}. Triggered while building the SORTBY token list for a Step.
Common situations: Defaulting both flags true. UI toggles that allow both ASC and DESC selected. Reusing a SortBy value and flipping one flag without clearing the other.
Related errors
- redis: FT.AGGREGATE COLLECT: empty field name in SortBy
- redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclu
- FT.AGGREGATE: Steps cannot be combined with the deprecated L
- FT.AGGREGATE: LOADALL and LOAD are mutually exclusive
- FT.AGGREGATE: each step must set exactly one of Load, Apply,
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/9cce9db25af69e09.json.
Report an issue: GitHub.