redis/go-redis · error

FT.HYBRID: ASC and DESC are mutually exclusive

Error message

FT.HYBRID: ASC and DESC are mutually exclusive

What it means

FT.HYBRID SORTBY validation error raised client-side. Within a single SortBy entry, Asc and Desc are boolean flags for sort direction; setting both simultaneously is contradictory, so go-redis rejects the options before issuing the command. Exactly one direction (or neither, which defaults to server behavior) must be set.

Solutions

  1. Set only one of Asc or Desc to true for each SortBy entry
  2. Derive the direction from a single source: Asc: dir == "asc", Desc: dir == "desc" so only one can be true
  3. Normalize options after deserialization: if both are set, reset one to false

Example fix

// before
sortBy: FTHybridSortBy{FieldName: "score", Asc: true, Desc: true}
// after
sortBy: FTHybridSortBy{FieldName: "score", Desc: true}
Defensive patterns

Strategy: validation

Validate before calling

for _, s := range opts.SortBy {
    if s.Asc && s.Desc { return fmt.Errorf("SortBy %q has both Asc and Desc", s.FieldName) }
}

Try / catch

cmd := rdb.FTHybrid(ctx, opts, args...)
if err := cmd.Err(); err != nil {
    if strings.Contains(err.Error(), "mutually exclusive") { /* normalize sort flags and retry */ }
    return err
}

Prevention

When it happens

Trigger: Passing an FTHybridSortBy entry where both Asc: true and Desc: true are set for the same FieldName.

Common situations: Loading sort configuration from a struct or config file where both direction booleans are populated, or merging user-supplied sort options where defaults set Asc=true and user input sets Desc=true.

Related errors


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/c043c73718f343a2. Report an issue: GitHub.

Appendix: source

Thrown at search_commands.go:4001

			if options.GroupBy.ReduceFunc != "" {
				args = append(args, "REDUCE", options.GroupBy.ReduceFunc, options.GroupBy.ReduceCount)
				args = append(args, options.GroupBy.ReduceParams...)
			}
		}

		// Add APPLY transformations
		for _, apply := range options.Apply {
			args = append(args, "APPLY", apply.Expression, "AS", apply.AsField)
		}

		// Add SORTBY
		if len(options.SortBy) > 0 {
			sortByOptions := []interface{}{}
			for _, sortBy := range options.SortBy {
				sortByOptions = append(sortByOptions, sortBy.FieldName)
				if sortBy.Asc && sortBy.Desc {
					cmd := newFTHybridCmd(ctx, options, args...)
					cmd.SetErr(fmt.Errorf("FT.HYBRID: ASC and DESC are mutually exclusive"))
					return cmd
				}
				if sortBy.Asc {
					sortByOptions = append(sortByOptions, "ASC")
				}
				if sortBy.Desc {
					sortByOptions = append(sortByOptions, "DESC")
				}
			}
			args = append(args, "SORTBY", len(sortByOptions))
			args = append(args, sortByOptions...)
		}

		// Add FILTER (post-filter)
		if options.Filter != "" {
			args = append(args, "FILTER", options.Filter)
		}

View on GitHub (pinned to c5cad058c7)