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
- 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.
- If the field name is derived from input, validate it is non-empty and not only '@' before adding it to SortBy.
- 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
- Validate every SortBy.FieldName is non-empty (after stripping leading '@') before passing the COLLECT config to NewCollectReducer.
- Treat field names from external input as untrusted: reject empty or '@'-only strings at the boundary.
- Unit-test the COLLECT builder with the exact SortBy slice your app produces.
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
- redis: FT.AGGREGATE COLLECT: ASC and DESC are mutually exclu
- FT.AGGREGATE: Collect must follow a GroupBy step
- 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/5518d334f096b93e.json.
Report an issue: GitHub.