go-redis/redis · error

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

Error message

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

What it means

Returned from NewCollectReducer (and surfaced via Collect/Run) when a field name in FTAggregateCollect.Fields is empty after stripping leading '@' characters. COLLECT requires concrete field names to project; an empty/whitespace/'@' entry cannot be rendered as @<name>.

Source

Thrown at search_collect.go:94

	return "@" + strings.TrimLeft(name, "@")
}

// buildCollectArgs renders a FTAggregateCollect into the reducer argument
// token list (everything after "REDUCE COLLECT <narg>", excluding AS <alias>).
// The serializer computes <narg> as len(args), which matches the COLLECT
// contract: narg counts every FIELDS/DISTINCT/SORTBY/LIMIT token.
func buildCollectArgs(o FTAggregateCollect) ([]interface{}, error) {
	args := make([]interface{}, 0, 8)

	// FIELDS (required): either * or a counted list of @-names.
	switch {
	case o.FieldsAll:
		args = append(args, "FIELDS", "*")
	case len(o.Fields) > 0:
		args = append(args, "FIELDS", len(o.Fields))
		for _, f := range o.Fields {
			if strings.TrimLeft(f, "@") == "" {
				return nil, fmt.Errorf("redis: FT.AGGREGATE COLLECT: empty field name in Fields")
			}
			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, "@") == "" {

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Filter the Fields slice to drop entries that are empty or contain only '@' before constructing FTAggregateCollect.
  2. Validate each field name with strings.TrimSpace(strings.TrimLeft(f, "@")) != "" before use.
  3. Fix the upstream producer so it never yields empty field names.

Example fix

// before
collect := redis.FTAggregateCollect{Fields: []string{"@sku", "", "@price"}}
b.GroupBy("@order").Collect(collect)

// after
fields := []string{"@sku", "@price"}
collect := redis.FTAggregateCollect{Fields: fields}
b.GroupBy("@order").Collect(collect)
Defensive patterns

Strategy: validation

Validate before calling

for _, f := range collect.Fields {
    if strings.TrimSpace(strings.TrimLeft(f, "@")) == "" {
        return fmt.Errorf("empty field name in COLLECT.Fields: %q", f)
    }
}

Try / catch

if _, err := redis.NewCollectReducer(collect); err != nil {
    if strings.Contains(err.Error(), "empty field name") {
        // sanitize the Fields slice and retry
    }
}

Prevention

When it happens

Trigger: Passing FTAggregateCollect{Fields: []string{"", "@"}} or any entry whose strings.TrimLeft(f, "@") is empty. The check fires at reducer construction (NewCollectReducer) or via AggregateBuilder.Collect.

Common situations: Field list built dynamically from struct tags/JSON that produced empty strings; a stray "@" placeholder; trailing empty element from a strings.Split on a trailing delimiter; copy-paste leaving an empty slot.

Related errors


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