redis/go-redis · error

invalid total_results format

Error message

invalid total_results format

What it means

The FT.HYBRID reply parser expects a numeric field "total_results" in the reply map. If that key is missing or its value is not an int64, the client returns this error. It signals the server reply does not follow the expected FT.HYBRID result structure.

Source

Thrown at search_commands.go:3066

	}

	// Handle cursor result
	if withCursor {
		searchCursorID, ok1 := resultMap["SEARCH"].(int64)
		vsimCursorID, ok2 := resultMap["VSIM"].(int64)
		if !ok1 || !ok2 {
			return FTHybridResult{}, nil, fmt.Errorf("invalid cursor result format")
		}
		return FTHybridResult{}, &FTHybridCursorResult{
			SearchCursorID: int(searchCursorID),
			VsimCursorID:   int(vsimCursorID),
		}, nil
	}

	// Parse regular result
	totalResults, ok := resultMap["total_results"].(int64)
	if !ok {
		return FTHybridResult{}, nil, fmt.Errorf("invalid total_results format")
	}

	resultsData, ok := resultMap["results"].([]interface{})
	if !ok {
		return FTHybridResult{}, nil, fmt.Errorf("invalid results format")
	}

	// Parse each result item
	results := make([]map[string]interface{}, 0, len(resultsData))
	for _, item := range resultsData {
		// Try parsing as map[string]interface{} first (RESP3 format)
		if itemMap, ok := item.(map[string]interface{}); ok {
			results = append(results, itemMap)
			continue
		}

		// Try parsing as map[interface{}]interface{} (alternative RESP3 format)
		if rawMap, ok := item.(map[interface{}]interface{}); ok {

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Align go-redis and server versions so the FT.HYBRID reply contract matches
  2. Use Protocol: 3 to get the RESP3 map reply path the parser expects
  3. Inspect the raw reply (ProcessHook or MONITOR) to confirm whether "total_results" exists and its type
  4. Fix mocks to include "total_results" as int64

Example fix

// before
map[interface{}]interface{}{"results": []interface{}{}}
// after
map[interface{}]interface{}{"total_results": int64(0), "results": []interface{}{}}
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: ensure the endpoint responds to a basic FT.HYBRID call before relying on it
_, err := rdb.Do(ctx, "FT.HYBRID", "idx", "QUERY", "*", "VSIM", "vec", "->", "1").Result()

Type guard

func hasTotalResults(m map[interface{}]interface{}) bool {
    _, ok := m["total_results"].(int64)
    return ok
}

Try / catch

res, _, err := hybridCmd.Result()
if err != nil && strings.Contains(err.Error(), "invalid total_results format") {
    // capture raw reply with a ProcessHook for bug report; treat as server incompatibility
    return ErrReplyShapeUnsupported
}

Prevention

When it happens

Trigger: Calling FTHybrid against a server whose reply omits "total_results" or returns it with a different type/name (different RediSearch build, proxy rewrite, or RESP protocol mismatch).

Common situations: Version skew between go-redis and the Redis stack server; a compatibility layer or RESP proxy renaming/re-typing fields; tests with hand-built maps lacking the key.

Related errors


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