redis/go-redis · error

invalid document fields format

Error message

invalid document fields format

What it means

The final element of each FT.SEARCH (RESP2) document entry must be the fields array (a []interface{} of key/value pairs), or nil when the document was deleted mid-index (which go-redis maps to doc.Error). Any other type at that position makes the parser fail with this error.

Source

Thrown at search_commands.go:2667

			}
		}

		if withSortKeys && i < len(data) {
			if sortKey, ok := data[i].(string); ok {
				doc.SortKey = &sortKey
				i++
			}
		}

		if i < len(data) {
			fields, ok := data[i].([]interface{})
			if !ok {
				if data[i] == proto.Nil || data[i] == nil {
					doc.Error = proto.Nil
					doc.Fields = map[string]string{}
					fields = []interface{}{}
				} else {
					return FTSearchResult{}, fmt.Errorf("invalid document fields format")
				}
			}

			for j := 0; j < len(fields); j += 2 {
				key, ok := fields[j].(string)
				if !ok {
					return FTSearchResult{}, fmt.Errorf("invalid field key format")
				}
				value, ok := fields[j+1].(string)
				if !ok {
					return FTSearchResult{}, fmt.Errorf("invalid field value format")
				}
				doc.Fields[key] = value
			}
			i++
		}

		results = append(results, doc)

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Check the raw reply's element layout and confirm each doc ends with an array or nil
  2. Make FTSearchOptions flags match the FT.SEARCH arguments actually issued
  3. Fix proxies/hooks/mocks that reshape the reply
  4. Upgrade to matched go-redis/RediSearch versions

Example fix

// before: mock uses a map for fields
mock := []interface{}{int64(1), "doc:1", map[string]string{"t": "v"}}

// after: fields must be a flat array
cmd := rdb.FTSearch(ctx, "idx", "q")
mock := []interface{}{int64(1), "doc:1", []interface{}{"t", "v"}}
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the trailing element of each doc entry is an array or nil
last := mockDocEntry[len(mockDocEntry)-1]
switch last.(type) {
case []interface{}, nil:
default:
	panic("fields element must be []interface{} or nil")
}

Type guard

func isFieldsElement(v interface{}) bool {
	if v == nil {
		return true // deleted doc case
	}
	_, ok := v.([]interface{})
	return ok
}

Try / catch

res, err := rdb.FTSearch(ctx, "idx", q).Result()
if err != nil {
	if strings.Contains(err.Error(), "invalid document fields") {
		log.Printf("unexpected fields element in FT.SEARCH reply: %v", err)
		return emptyDocs, nil
	}
	return err
}

Prevention

When it happens

Trigger: FTSearch reply where the fields element is a string/map/int instead of an array or nil — typically a reply shape shifted by missing WithScores/WithPayloads elements, a proxy rewriting the reply, or a malformed mock.

Common situations: Options mismatch (NoContent false but server sent no fields array); custom middleware; non-RediSearch server; hand-crafted test data using a map for fields.

Related errors


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