{"id":"133677286359ffcb","repo":"go-redis/redis","slug":"ft-search-asc-and-desc-are-mutually-exclusive","errorCode":null,"errorMessage":"FT.SEARCH: ASC and DESC are mutually exclusive","messagePattern":"FT\\.SEARCH: ASC and DESC are mutually exclusive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_commands.go","lineNumber":3377,"sourceCode":"\t\t}\n\t\tif options.Expander != \"\" {\n\t\t\tqueryArgs = append(queryArgs, \"EXPANDER\", options.Expander)\n\t\t}\n\t\tif options.Scorer != \"\" {\n\t\t\tqueryArgs = append(queryArgs, \"SCORER\", options.Scorer)\n\t\t}\n\t\tif options.ExplainScore {\n\t\t\tqueryArgs = append(queryArgs, \"EXPLAINSCORE\")\n\t\t}\n\t\tif options.Payload != \"\" {\n\t\t\tqueryArgs = append(queryArgs, \"PAYLOAD\", options.Payload)\n\t\t}\n\t\tif options.SortBy != nil {\n\t\t\tqueryArgs = append(queryArgs, \"SORTBY\")\n\t\t\tfor _, sortBy := range options.SortBy {\n\t\t\t\tqueryArgs = append(queryArgs, sortBy.FieldName)\n\t\t\t\tif sortBy.Asc && sortBy.Desc {\n\t\t\t\t\treturn nil, fmt.Errorf(\"FT.SEARCH: ASC and DESC are mutually exclusive\")\n\t\t\t\t}\n\t\t\t\tif sortBy.Asc {\n\t\t\t\t\tqueryArgs = append(queryArgs, \"ASC\")\n\t\t\t\t}\n\t\t\t\tif sortBy.Desc {\n\t\t\t\t\tqueryArgs = append(queryArgs, \"DESC\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif options.SortByWithCount {\n\t\t\t\tqueryArgs = append(queryArgs, \"WITHCOUNT\")\n\t\t\t}\n\t\t}\n\t\tif options.LimitOffset >= 0 && options.Limit > 0 {\n\t\t\tqueryArgs = append(queryArgs, \"LIMIT\", options.LimitOffset, options.Limit)\n\t\t}\n\t\tif options.Params != nil {\n\t\t\tqueryArgs = append(queryArgs, \"PARAMS\", len(options.Params)*2)\n\t\t\tfor key, value := range options.Params {","sourceCodeStart":3359,"sourceCodeEnd":3395,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_commands.go#L3359-L3395","documentation":"Returned by the FTSearchQuery query builder (search_commands.go:3376-3378): for any entry in options.SortBy, you cannot set both Asc:true and Desc:true on the same SortBy element. The library refuses to emit a contradictory SORTBY clause.","triggerScenarios":"Constructing a SearchQuery via FTSearchQuery with a redis.FTSearchSortBy{Asc: true, Desc: true, ...} entry.","commonSituations":"Copy-paste from another call where the opposite flag was set, or programmatic construction that defaulted both booleans true.","solutions":["Set exactly one of Asc or Desc per SortBy entry.","Leave both false for the server default direction.","Validate options.SortBy before calling FTSearchQuery."],"exampleFix":"// before\nq, err := redis.FTSearchQuery(\"*\", &redis.FTSearchOptions{\n    SortBy: []redis.FTSearchSortBy{{FieldName: \"ts\", Asc: true, Desc: true}},\n})\n// after\nq, err := redis.FTSearchQuery(\"*\", &redis.FTSearchOptions{\n    SortBy: []redis.FTSearchSortBy{{FieldName: \"ts\", Asc: true}},\n})","handlingStrategy":"validation","validationCode":"// Reject contradictory SortBy entries before building the query.\nfor _, s := range opts.SortBy {\n    if s.Asc && s.Desc {\n        return fmt.Errorf(\"SortBy %q: pick ASC or DESC, not both\", s.FieldName)\n    }\n}","typeGuard":null,"tryCatchPattern":"q, err := redis.FTSearchQuery(qs, opts)\nif err != nil && strings.Contains(err.Error(), \"ASC and DESC are mutually exclusive\") {\n    // fix the SortBy entry that has both flags set\n}","preventionTips":["Set at most one of Asc/Desc per FTSearchSortBy entry.","Centralize SortBy construction in a helper that enforces the rule.","Validate options before calling FTSearchQuery."],"tags":["redisearch","ft-search","argument-validation","sortby"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}