redis/go-redis · error
invalid document ID format
Error message
invalid document ID format
What it means
In the FT.SEARCH (RESP2) reply, after the total, results come as alternating document-ID / fields pairs. The parser expects each ID to be a bulk string (Go string); a non-string element where an ID was expected aborts parsing with this error.
Source
Thrown at search_commands.go:2620
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
}
if withScores && i < len(data) {
if scoreStr, ok := data[i].(string); ok {
score, err := strconv.ParseFloat(scoreStr, 64)
if err != nil {
return FTSearchResult{}, fmt.Errorf("invalid score format")View on GitHub (pinned to c5cad058c7)
Solutions
- Ensure FTSearchOptions (NoContent, WithScores, WithPayloads, WithSortKeys) match the flags actually sent to the server
- Print the raw reply (RawResult) and check element ordering: total, id, [score], [payload], [sortkey], fields
- Fix any proxy or mock producing a shifted reply
- Upgrade go-redis and RediSearch to compatible versions
Example fix
// before: mock omits the fields array the flags expect
mock := []interface{}{int64(1), "doc:1"}
// after: include the fields array
cmd := rdb.FTSearch(ctx, "idx", "q")
mock := []interface{}{int64(1), "doc:1", []interface{}{"title", "hi"}} Defensive patterns
Strategy: validation
Validate before calling
// Keep reply-shaping flags consistent between what you send and parse
opts := &redis.FTSearchOptions{NoContent: false, WithScores: true}
// mock layout must then be: [int64 total, id, score, fields, ...] Type guard
func isStringAt(data []interface{}, i int) bool {
return i < len(data)
// caller: id, ok := data[i].(string)
} Try / catch
res, err := rdb.FTSearch(ctx, "idx", q, opts).Result()
if err != nil {
if strings.Contains(err.Error(), "invalid document ID") {
log.Printf("reply misaligned with options %v: %v", opts, err)
}
return err
} Prevention
- Never change FTSearchOptions without re-checking the expected reply layout
- Derive mocks from real captured replies (RawResult)
- Avoid middleware that inserts/removes reply elements
When it happens
Trigger: FTSearch reply whose element at an odd offset is not a string — reply misalignment caused by withScores/withPayloads/withSortKeys options not matching the server's response, or a proxy/malformed mock producing the wrong layout.
Common situations: Client options (NoContent/WithScores/WithPayloads/WithSortKeys) differ from what the server was asked for, shifting element positions; intermediaries rewriting replies; bad mocks.
Related errors
- unexpected search result format
- invalid total results format
- invalid score format
- invalid document fields format
- invalid field value format
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/44291937dd1c16d0.
Report an issue: GitHub.