redis/go-redis · error
redis: can't parse reply=%.100q reading string
Error message
redis: can't parse reply=%.100q reading string
What it means
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.
Source
Thrown at internal/proto/reader.go:690
switch line[0] {
case RespStatus, RespInt, RespFloat:
return string(line[1:]), nil
case RespString:
return r.readStringReply(line)
case RespBool:
b, err := r.readBool(line)
return strconv.FormatBool(b), err
case RespVerbatim:
return r.readVerb(line)
case RespBigInt:
b, err := r.readBigInt(line)
if err != nil {
return "", err
}
return b.String(), nil
}
return "", fmt.Errorf("redis: can't parse reply=%.100q reading string", line)
}
func (r *Reader) ReadBool() (bool, error) {
s, err := r.ReadString()
if err != nil {
return false, err
}
return s == "OK" || s == "1" || s == "true", nil
}
func (r *Reader) ReadSlice() ([]interface{}, error) {
line, err := r.ReadLine()
if err != nil {
return nil, err
}
return r.readSlice(line)
}
View on GitHub (pinned to c5cad058c7)
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
Example fix
// before
opt := &redis.Options{Addr: addr}
// after: opt in RESP3 so map/verbatim replies parse correctly
opt := &redis.Options{Addr: addr, Protocol: 3} Defensive patterns
Strategy: type-guard
Validate before calling
// confirm the command returns a string at this position // e.g. redis-cli --raw XINFO GROUPS mystream
Type guard
func isStringableReply(t byte) bool {
return t == '+' || t == '$' || t == '=' || t == ':'
} Try / catch
s, err := reader.ReadString()
if err != nil {
if strings.Contains(err.Error(), "can't parse reply") {
logger.Warn("unexpected reply type; resetting conn", "err", err)
_ = conn.Close()
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- redis: can't parse reply=%.100q reading string into buffer
- redis: can't parse array/set/push reply: %.100q
- redis: can't parse map reply: %.100q
- redis: can't read raw reply: %.100q
- redis: got %d elements in the array, wanted %d
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/d71607255437d1f1.
Report an issue: GitHub.