{"record":{"id":"d71607255437d1f1","repo":"redis/go-redis","slug":"redis-can-t-parse-reply-100q-reading-string","errorCode":null,"errorMessage":"redis: can't parse reply=%.100q reading string","messagePattern":"redis: can't parse reply=%\\.100q reading string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":690,"sourceCode":"\n\tswitch line[0] {\n\tcase RespStatus, RespInt, RespFloat:\n\t\treturn string(line[1:]), nil\n\tcase RespString:\n\t\treturn r.readStringReply(line)\n\tcase RespBool:\n\t\tb, err := r.readBool(line)\n\t\treturn strconv.FormatBool(b), err\n\tcase RespVerbatim:\n\t\treturn r.readVerb(line)\n\tcase RespBigInt:\n\t\tb, err := r.readBigInt(line)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn b.String(), nil\n\t}\n\treturn \"\", fmt.Errorf(\"redis: can't parse reply=%.100q reading string\", line)\n}\n\nfunc (r *Reader) ReadBool() (bool, error) {\n\ts, err := r.ReadString()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treturn s == \"OK\" || s == \"1\" || s == \"true\", nil\n}\n\nfunc (r *Reader) ReadSlice() ([]interface{}, error) {\n\tline, err := r.ReadLine()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.readSlice(line)\n}\n","sourceCodeStart":672,"sourceCodeEnd":708,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/internal/proto/reader.go#L672-L708","documentation":"ReadString could not interpret the reply line as any string-compatible RESP type. The parser exhausted its switch over known type bytes (simple string, error, integer, bulk, verbatim, nil, bool variants) and falls through to this error, including the offending line truncated to 100 chars. It indicates the reply type is unexpected for a string read or the stream is desynchronized.","triggerScenarios":"readReply or stream parsers (readXMessage, stringInterfaceMapParser, readStreamGroups, readXInfoStream*P*) encountering a reply element that is not a string where one is expected — e.g. RESP3 map/verbatim reply under RESP2 mode, or a NIL element where a string is required.","commonSituations":"XREADGROUP/XINFO replies parsed against an unexpected server version output; RESP2 vs RESP3 mismatch (Protocol option) so maps arrive as arrays with different shapes; corrupt stream after an interrupted connection reuse.","solutions":["Set Protocol: 3 in Options if the server emits RESP3-only types (maps, verbatim strings) your code path parses as strings","Check that the command used actually returns a string at this position (inspect with redis-cli --raw)","Ensure no earlier reply was dropped, which desyncs all subsequent line parsing","Upgrade go-redis if a newer Redis version changed a command's reply shape"],"exampleFix":"// before\nopt := &redis.Options{Addr: addr}\n// after: opt in RESP3 so map/verbatim replies parse correctly\nopt := &redis.Options{Addr: addr, Protocol: 3}","handlingStrategy":"type-guard","validationCode":"// confirm the command returns a string at this position\n// e.g. redis-cli --raw XINFO GROUPS mystream","typeGuard":"func isStringableReply(t byte) bool {\n  return t == '+' || t == '$' || t == '=' || t == ':'\n}","tryCatchPattern":"s, err := reader.ReadString()\nif err != nil {\n  if strings.Contains(err.Error(), \"can't parse reply\") {\n    logger.Warn(\"unexpected reply type; resetting conn\", \"err\", err)\n    _ = conn.Close()\n  }\n  return err\n}","preventionTips":["Set Protocol: 3 when consuming RESP3-only types","Keep server version in sync with parser expectations for XINFO/XREADGROUP","Never reuse a connection after a partial read","Check cmd.Err() before interpreting parsed payloads"],"tags":["go-redis","resp-protocol","parsing","streams"],"backgroundTag":"resp-reply-parse-failure","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}