go-redis/redis · error

redis: FT.AGGREGATE COLLECT: empty field name in SortBy

Error message

redis: FT.AGGREGATE COLLECT: empty field name in SortBy

What it means

Returned by NewCollectReducer (and AggregateBuilder.Collect) when a SortBy entry in FTAggregateCollect has a FieldName that is empty or consists only of '@' characters. The COLLECT reducer requires every SORTBY field to resolve to a concrete @<name> token, so the builder rejects names that normalize to the empty string before any bytes go to Redis. This is purely local API-misuse validation; the server is never contacted.

Source

Thrown at search_collect.go:113

			}
			args = append(args, ensureAtPrefix(f))
		}
	default:
		return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT requires FieldsAll or a non-empty Fields list")
	}

	// DISTINCT (optional, forward-compatible).
	if o.Distinct {
		args = append(args, "DISTINCT")
	}

	// SORTBY (optional). sort_narg counts each field plus its optional
	// direction token.
	if len(o.SortBy) > 0 {
		sortTokens := make([]interface{}, 0, len(o.SortBy)*2)
		for _, s := range o.SortBy {
			if strings.TrimLeft(s.FieldName, "@") == "" {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: empty field name in SortBy")
			}
			if s.Asc && s.Desc {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclusive")
			}
			sortTokens = append(sortTokens, ensureAtPrefix(s.FieldName))
			switch {
			case s.Desc:
				sortTokens = append(sortTokens, "DESC")
			case s.Asc:
				sortTokens = append(sortTokens, "ASC")
				// neither set: ASC is the server default; emit nothing.
			}
		}
		args = append(args, "SORTBY", len(sortTokens))
		args = append(args, sortTokens...)
	}

	// LIMIT (optional).

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Ensure every FTAggregateSortBy entry in FTAggregateCollect.SortBy has a non-empty FieldName (e.g. "price", "@timestamp"); the builder tolerates any number of leading '@' but not an empty result.
  2. If the field name is derived from input, validate it is non-empty and not only '@' before adding it to SortBy.
  3. Grep for `.FieldName = ""` or unset SortBy structs in the call site constructing the COLLECT reducer.

Example fix

// before
NewCollectReducer(FTAggregateCollect{
    Fields: []string{"sku"},
    SortBy: []FTAggregateSortBy{{FieldName: "", Asc: true}},
})
// after
NewCollectReducer(FTAggregateCollect{
    Fields: []string{"sku"},
    SortBy: []FTAggregateSortBy{{FieldName: "price", Asc: true}},
})
Defensive patterns

Strategy: validation

Validate before calling

func validCollectSortBy(sb []FTAggregateSortBy) error {
	for i, s := range sb {
		if strings.TrimLeft(s.FieldName, "@") == "" {
			return fmt.Errorf("SortBy[%d]: empty field name", i)
		}
	}
	return nil
}

// call before NewCollectReducer:
if err := validCollectSortBy(c.SortBy); err != nil { return err }

Type guard

func hasNonEmptyFieldName(s FTAggregateSortBy) bool {
	return strings.TrimLeft(s.FieldName, "@") != ""
}

Prevention

When it happens

Trigger: Calling NewCollectReducer(FTAggregateCollect{SortBy: []FTAggregateSortBy{{FieldName: ""}}}) or with FieldName set to "@", "@@@", etc. Also reached via AggregateBuilder.Collect with the same misconfiguration. Triggered at argument-build time, before the FT.AGGREGATE command is serialized.

Common situations: Field name coming from an unmarshalled config/struct that left the value zero. Dynamically building SortBy from user input where a field was dropped. Copy-pasting a SortBy entry and forgetting to set FieldName. Mistakenly passing an alias instead of the source field name as an empty string.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/5518d334f096b93e.json. Report an issue: GitHub.