redis/go-redis · error
FT.SEARCH: ASC and DESC are mutually exclusive
Error message
FT.SEARCH: ASC and DESC are mutually exclusive
What it means
go-redis validates FTSearchOptions before sending FT.SEARCH: within a single SortBy entry, Asc and Desc cannot both be true since they are mutually exclusive sort directions. This is a client-side argument-validation error raised while building the command arguments, before anything reaches the server.
Source
Thrown at search_commands.go:3403
}
if options.Expander != "" {
queryArgs = append(queryArgs, "EXPANDER", options.Expander)
}
if options.Scorer != "" {
queryArgs = append(queryArgs, "SCORER", options.Scorer)
}
if options.ExplainScore {
queryArgs = append(queryArgs, "EXPLAINSCORE")
}
if options.Payload != "" {
queryArgs = append(queryArgs, "PAYLOAD", options.Payload)
}
if options.SortBy != nil {
queryArgs = append(queryArgs, "SORTBY")
for _, sortBy := range options.SortBy {
queryArgs = append(queryArgs, sortBy.FieldName)
if sortBy.Asc && sortBy.Desc {
return nil, fmt.Errorf("FT.SEARCH: ASC and DESC are mutually exclusive")
}
if sortBy.Asc {
queryArgs = append(queryArgs, "ASC")
}
if sortBy.Desc {
queryArgs = append(queryArgs, "DESC")
}
}
if options.SortByWithCount {
queryArgs = append(queryArgs, "WITHCOUNT")
}
}
if options.LimitOffset >= 0 && options.Limit > 0 {
queryArgs = append(queryArgs, "LIMIT", options.LimitOffset, options.Limit)
}
if options.Params != nil {
queryArgs = append(queryArgs, "PARAMS", len(options.Params)*2)
for key, value := range options.Params {View on GitHub (pinned to c5cad058c7)
Solutions
- Set only one direction flag per SortBy entry: Asc: true or Desc: true, not both
- Leave both false to use the server default (ASC) when no explicit direction is needed
- If options come from user input or config, sanitize them: if both are set, prefer one (e.g. Desc) or reject the input earlier
- Review option-merging code so setting one flag resets the other
Example fix
// before
sortBy := redis.SearchSortBy{FieldName: "age", Asc: true, Desc: true}
// after
sortBy := redis.SearchSortBy{FieldName: "age", Desc: true} Defensive patterns
Strategy: validation
Validate before calling
func sanitizeSortBy(sortBy []redis.SearchSortBy) []redis.SearchSortBy {
out := make([]redis.SearchSortBy, len(sortBy))
for i, s := range sortBy {
if s.Asc && s.Desc { s.Desc = false } // or reject
out[i] = s
}
return out
} Type guard
func sortDirectionValid(s redis.SearchSortBy) bool {
return !(s.Asc && s.Desc)
} Try / catch
args, err := redis.NewFTSearchQuery("idx", query, opts).Args() // or call FTSearch
if err != nil {
if strings.Contains(err.Error(), "mutually exclusive") {
return fmt.Errorf("bad sort config: %w", err)
}
return err
} Prevention
- Never set both Asc and Desc; treat them as an enum in your app layer
- Sanitize merged/user-supplied options before building queries
- Add a unit test asserting at most one direction flag per SortBy entry
When it happens
Trigger: Calling FTSearch (or FTSearchWithArgs) with options.SortBy where one SearchSortBy has both Asc: true and Desc: true set.
Common situations: Copy-paste or programmatic construction of sort options where both flags are set from separate booleans; merging user-supplied options without clearing the opposite flag; misunderstanding that the zero value (neither set) is the default ASC.
Related errors
- FT.AGGREGATE: SortByMax must follow a SortBy step
- redis: FT.AGGREGATE COLLECT: empty field name in Fields
- redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empt
- redis: FT.AGGREGATE COLLECT: empty field name in SortBy
- redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclu
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/dfb343fa23c1a1fc.
Report an issue: GitHub.