redis/go-redis · error

redis: can't parse reply=%.100q reading string into buffer

Error message

redis: can't parse reply=%.100q reading string into buffer

What it means

The reader received a reply line it could not interpret as a string when filling a caller-supplied buffer (ReadStringInto). After failing all known reply types (simple string, bulk string, verbatim, integer, etc.) it gives up and reports the raw line truncated to 100 chars. This almost always means the bytes on the wire are not a valid RESP reply for this position, or the reply type is one the parser does not support here.

Source

Thrown at internal/proto/reader.go:664

		// Slow path: buffer is exactly large enough for the payload only, so
		// read the payload into it and discard the CRLF separately.
		if _, err := io.ReadFull(r.rd, buf[:n]); err != nil {
			return 0, err
		}
		if _, err := r.rd.Discard(2); err != nil {
			return 0, err
		}
		return n, nil

	case RespInt, RespFloat:
		s := line[1:]
		if len(s) > len(buf) {
			return 0, fmt.Errorf("redis: buffer too small: need %d bytes, have %d", len(s), len(buf))
		}
		return copy(buf, s), nil
	}

	return 0, fmt.Errorf("redis: can't parse reply=%.100q reading string into buffer", line)
}

func (r *Reader) ReadString() (string, error) {
	line, err := r.ReadLine()
	if err != nil {
		return "", err
	}

	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)

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Ensure every issued command consumes exactly its reply — a desync from skipped replies makes subsequent reads parse garbage
  2. Verify the endpoint is a real Redis server and no proxy rewrites responses
  3. Use Protocol: 3 consistently if the server requires RESP3 types (maps/verbatim) your caller parses as strings
  4. Log the %.100q payload in the error to identify the unexpected type byte

Example fix

// before: reading a custom command reply as a fixed-size string buffer
cmd := NewStringCmd(ctx, "MYCMD")
reader.ReadStringInto(buf)
// after: parse according to the actual reply type
val, err := reader.ReadString()
if err != nil { return err }
copy(buf, val)
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure reply kind before reading into buffer
line, err := r.PeekReplyType() // or read via ReadString first
if err != nil { return err }
if len(buf) < neededLen { return ErrBufferTooSmall }

Type guard

func isStringReply(t byte) bool {
  return t == '+' || t == '$' || t == '=' || t == ':'
}

Try / catch

n, err := reader.ReadStringInto(buf)
var perr *redis.ProtoError
if errors.As(err, &perr) {
  // bad reply type or too-small buffer: log perr and reset connection
  _ = conn.Close()
  return fmt.Errorf("string-into parse failed: %w", err)
}

Prevention

When it happens

Trigger: Calling ReadStringInto on a connection whose next reply is not a string-shaped RESP type (e.g. an array, map, nil, or unknown type byte), or when the line is corrupt/garbage from a protocol desync (e.g. after a partial read, a proxy injecting data, or talking to a non-Redis server).

Common situations: RESP proxies or load balancers mangling traffic; protocol desync after a previous command's reply was mis-consumed; pointing the client at a non-Redis service; RESP2 servers replying with types the caller didn't expect for custom commands.

Related errors


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