redis/go-redis · error

invalid cursor result format

Error message

invalid cursor result format

What it means

When FT.HYBRID is run WITHCURSOR, the reply map must contain integer cursor IDs under the keys "SEARCH" and "VSIM". If either key is missing or its value is not an int64, the client cannot construct the FTHybridCursorResult and returns this error. It means the cursor reply from the server did not match the expected two-cursor structure.

Source

Thrown at search_commands.go:3055

func parseFTHybrid(data []interface{}, withCursor bool) (FTHybridResult, *FTHybridCursorResult, error) {
	// Convert to map
	resultMap := make(map[string]interface{})
	for i := 0; i < len(data); i += 2 {
		if i+1 < len(data) {
			key, ok := data[i].(string)
			if !ok {
				return FTHybridResult{}, nil, fmt.Errorf("invalid key type at index %d", i)
			}
			resultMap[key] = data[i+1]
		}
	}

	// 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")
	}

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Upgrade go-redis (and/or the Redis stack server) so client and server agree on the FT.HYBRID cursor reply format
  2. Ensure Protocol: 3 is negotiated; cursor reply parsing may differ between RESP2 and RESP3
  3. Capture the raw reply via a hook/MONITOR and confirm the "SEARCH"/"VSIM" keys and their types
  4. Avoid proxies that rewrite reply values (e.g. converting int64 cursors to strings)
Defensive patterns

Strategy: fallback

Validate before calling

// only request cursor when the server build supports hybrid cursors
if !serverSupportsHybridCursor { opts.Cursor = false }

Type guard

func hasHybridCursorReply(m map[interface{}]interface{}) bool {
    _, ok1 := m["SEARCH"].(int64)
    _, ok2 := m["VSIM"].(int64)
    return ok1 && ok2
}

Try / catch

res, cursor, err := hybridCmd.Result()
if err != nil {
    if strings.Contains(err.Error(), "invalid cursor result format") {
        // fall back to non-cursor query without CURSOR option
        return runHybridWithoutCursor(ctx)
    }
    return err
}

Prevention

When it happens

Trigger: Calling FTHybrid with the Cursor option against a server that does not return the expected {"SEARCH": int64, "VSIM": int64} cursor map — e.g. a RediSearch version with different cursor reply keys, or a proxy rewriting the reply.

Common situations: Server upgrade/downgrade changing FT.HYBRID cursor reply layout; hitting a non-Redis-compatible endpoint that lacks hybrid cursor support; RESP2/RESP3 protocol mismatch altering value types (e.g. cursor returned as string).

Related errors


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