redis/go-redis · error

invalid total results format

Error message

invalid total results format

What it means

The first element of the FT.SEARCH (RESP2) reply must be the total number of results as a RESP integer (Go int64). If data[0] is not an int64, the reply does not follow the RediSearch wire format and this error is returned.

Solutions

  1. Verify server is RediSearch and the client Protocol option matches (3 for RESP3-aware setups)
  2. If using mocks, encode the total as int64, not int: []interface{}{int64(2), ...}
  3. Bypass or fix any reply-rewriting proxy/hook
  4. Capture the raw reply to see what data[0] actually is

Example fix

// before: mock uses int
mock := []interface{}{2, "doc:1", []interface{}{"title", "hi"}}

// after: mock uses int64 (RESP integers decode to int64)
mock := []interface{}{int64(2), "doc:1", []interface{}{"title", "hi"}}
Defensive patterns

Strategy: type-guard

Validate before calling

// Confirm RESP integers decode as int64 in your mocks
total, ok := mockReply[0].(int64)
if !ok {
	panic("mock total must be int64, not int")
}

Type guard

func isInt64(v interface{}) bool {
	_, ok := v.(int64)
	return ok
}

Try / catch

res, err := rdb.FTSearch(ctx, "idx", q).Result()
if err != nil {
	if strings.Contains(err.Error(), "invalid total results") {
		log.Printf("FT.SEARCH reply shape unexpected: %v", err)
		return fallbackResult
	}
	return err
}

Prevention

When it happens

Trigger: FTSearch reply where the total is a string, a map, or nil — typically because a proxy translated the reply, a non-RediSearch server answered, or a RESP2/RESP3 mismatch altered decoding.

Common situations: Client Protocol setting incompatible with the proxy; custom mock replies using int instead of int64 (Go type assertion fails: int != int64); older module versions.

Related errors


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

Appendix: source

Thrown at search_commands.go:2613

			if result.Suggestions != nil {
				val[i].Suggestions = slices.Clone(result.Suggestions)
			}
		}
	}
	return &FTSpellCheckCmd{
		baseCmd: cmd.cloneBaseCmd(),
		val:     val,
	}
}

func parseFTSearch(data []interface{}, noContent, withScores, withPayloads, withSortKeys bool) (FTSearchResult, error) {
	if len(data) < 1 {
		return FTSearchResult{}, fmt.Errorf("unexpected search result format")
	}

	total, ok := data[0].(int64)
	if !ok {
		return FTSearchResult{}, fmt.Errorf("invalid total results format")
	}

	var results []Document
	for i := 1; i < len(data); {
		docID, ok := data[i].(string)
		if !ok {
			return FTSearchResult{}, fmt.Errorf("invalid document ID format")
		}

		doc := Document{
			ID:     docID,
			Fields: make(map[string]string),
		}
		i++

		if noContent {
			results = append(results, doc)
			continue

View on GitHub (pinned to c5cad058c7)