{"record":{"id":"44291937dd1c16d0","repo":"redis/go-redis","slug":"invalid-document-id-format","errorCode":null,"errorMessage":"invalid document ID format","messagePattern":"invalid document ID format","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_commands.go","lineNumber":2620,"sourceCode":"\t\tval:     val,\n\t}\n}\n\nfunc parseFTSearch(data []interface{}, noContent, withScores, withPayloads, withSortKeys bool) (FTSearchResult, error) {\n\tif len(data) < 1 {\n\t\treturn FTSearchResult{}, fmt.Errorf(\"unexpected search result format\")\n\t}\n\n\ttotal, ok := data[0].(int64)\n\tif !ok {\n\t\treturn FTSearchResult{}, fmt.Errorf(\"invalid total results format\")\n\t}\n\n\tvar results []Document\n\tfor i := 1; i < len(data); {\n\t\tdocID, ok := data[i].(string)\n\t\tif !ok {\n\t\t\treturn FTSearchResult{}, fmt.Errorf(\"invalid document ID format\")\n\t\t}\n\n\t\tdoc := Document{\n\t\t\tID:     docID,\n\t\t\tFields: make(map[string]string),\n\t\t}\n\t\ti++\n\n\t\tif noContent {\n\t\t\tresults = append(results, doc)\n\t\t\tcontinue\n\t\t}\n\n\t\tif withScores && i < len(data) {\n\t\t\tif scoreStr, ok := data[i].(string); ok {\n\t\t\t\tscore, err := strconv.ParseFloat(scoreStr, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn FTSearchResult{}, fmt.Errorf(\"invalid score format\")","sourceCodeStart":2602,"sourceCodeEnd":2638,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/search_commands.go#L2602-L2638","documentation":"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.","triggerScenarios":"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.","commonSituations":"Client options (NoContent/WithScores/WithPayloads/WithSortKeys) differ from what the server was asked for, shifting element positions; intermediaries rewriting replies; bad mocks.","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"],"exampleFix":"// before: mock omits the fields array the flags expect\nmock := []interface{}{int64(1), \"doc:1\"}\n\n// after: include the fields array\ncmd := rdb.FTSearch(ctx, \"idx\", \"q\")\nmock := []interface{}{int64(1), \"doc:1\", []interface{}{\"title\", \"hi\"}}","handlingStrategy":"validation","validationCode":"// Keep reply-shaping flags consistent between what you send and parse\nopts := &redis.FTSearchOptions{NoContent: false, WithScores: true}\n// mock layout must then be: [int64 total, id, score, fields, ...]","typeGuard":"func isStringAt(data []interface{}, i int) bool {\n\treturn i < len(data)\n\t// caller: id, ok := data[i].(string)\n}","tryCatchPattern":"res, err := rdb.FTSearch(ctx, \"idx\", q, opts).Result()\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid document ID\") {\n\t\tlog.Printf(\"reply misaligned with options %v: %v\", opts, err)\n\t}\n\treturn err\n}","preventionTips":["Never change FTSearchOptions without re-checking the expected reply layout","Derive mocks from real captured replies (RawResult)","Avoid middleware that inserts/removes reply elements"],"tags":["go-redis","ftsearch","resp-parsing"],"backgroundTag":"resp-reply-parse-failed","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}