go-redis/redis · error

redis: invalid line

Error message

redis: invalid line

What it means

Returned by the RESP reader's Discard when it receives a zero-length line (reader.go:785-786). A well-formed RESP stream never has a bare empty line, so this signals protocol desynchronization — the reader and server have lost framing alignment, typically due to a corrupted stream, a man-in-the-middle/proxy mangling bytes, or reading past a closed connection.

Source

Thrown at internal/proto/reader.go:786

		return n / 2, nil
	default:
		return 0, fmt.Errorf("redis: can't parse map reply: %.100q", line)
	}
}

// DiscardNext read and discard the data represented by the next line.
func (r *Reader) DiscardNext() error {
	line, err := r.readLine()
	if err != nil {
		return err
	}
	return r.Discard(line)
}

// Discard the data represented by line.
func (r *Reader) Discard(line []byte) (err error) {
	if len(line) == 0 {
		return errors.New("redis: invalid line")
	}
	switch line[0] {
	case RespStatus, RespError, RespInt, RespNil, RespFloat, RespBool, RespBigInt:
		return nil
	}

	n, err := replyLen(line)
	if err != nil {
		if err == Nil {
			// A nil reply ($-1, =-1, !-1, *-1, %-1) carries no payload; the
			// header line was already consumed by readLine, so there is
			// nothing to discard. Falling through would Discard(n+2)==2 bytes
			// that belong to the next reply and desync the stream, matching
			// how readRawReplyBuf/readRawReplyWriteTo already treat Nil.
			return nil
		}
		return err
	}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. The connection is unusable after desync — let the pool close it and retry on a fresh connection.
  2. Verify you are talking to a real Redis (not a proxy that rewrites RESP), and that TLS/non-TLS matches.
  3. Check for network-level corruption or MTU/fragmentation issues.

Example fix

// the error implies the conn is desynced — drop it and retry
for i := 0; i < 3; i++ {
    err := client.Get(ctx, key).Err()
    if err == nil { break }
    if strings.Contains(err.Error(), "invalid line") { continue }
}
Defensive patterns

Strategy: retry

Validate before calling

// Cannot pre-validate a live stream. Ensure endpoint is real Redis and
// protocol/TLS matches the server.

Try / catch

for i := 0; i < 3; i++ {
    err := client.Get(ctx, key).Err()
    if err == nil { break }
    if strings.Contains(err.Error(), "invalid line") {
        // desync — let the pool discard this conn and retry
        continue
    }
    return err
}

Prevention

When it happens

Trigger: Discard(line) called with an empty byte slice during response parsing, e.g. after a partial read, a truncated reply, or a misbehaving proxy that injects/strip bytes. Often surfaces during DiscardNext/Discard of multi-bulk replies.

Common situations: A L7 proxy or RESP-mangling sidecar, TLS corruption, half-closed sockets, or a non-Redis endpoint answering on the Redis port.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/67c808b19fbfc2b7.json. Report an issue: GitHub.