{"id":"67c808b19fbfc2b7","repo":"go-redis/redis","slug":"redis-invalid-line","errorCode":null,"errorMessage":"redis: invalid line","messagePattern":"redis: invalid line","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":786,"sourceCode":"\t\treturn n / 2, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"redis: can't parse map reply: %.100q\", line)\n\t}\n}\n\n// DiscardNext read and discard the data represented by the next line.\nfunc (r *Reader) DiscardNext() error {\n\tline, err := r.readLine()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn r.Discard(line)\n}\n\n// Discard the data represented by line.\nfunc (r *Reader) Discard(line []byte) (err error) {\n\tif len(line) == 0 {\n\t\treturn errors.New(\"redis: invalid line\")\n\t}\n\tswitch line[0] {\n\tcase RespStatus, RespError, RespInt, RespNil, RespFloat, RespBool, RespBigInt:\n\t\treturn nil\n\t}\n\n\tn, err := replyLen(line)\n\tif err != nil {\n\t\tif err == Nil {\n\t\t\t// A nil reply ($-1, =-1, !-1, *-1, %-1) carries no payload; the\n\t\t\t// header line was already consumed by readLine, so there is\n\t\t\t// nothing to discard. Falling through would Discard(n+2)==2 bytes\n\t\t\t// that belong to the next reply and desync the stream, matching\n\t\t\t// how readRawReplyBuf/readRawReplyWriteTo already treat Nil.\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/internal/proto/reader.go#L768-L804","documentation":"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.","triggerScenarios":"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.","commonSituations":"A L7 proxy or RESP-mangling sidecar, TLS corruption, half-closed sockets, or a non-Redis endpoint answering on the Redis port.","solutions":["The connection is unusable after desync — let the pool close it and retry on a fresh connection.","Verify you are talking to a real Redis (not a proxy that rewrites RESP), and that TLS/non-TLS matches.","Check for network-level corruption or MTU/fragmentation issues."],"exampleFix":"// the error implies the conn is desynced — drop it and retry\nfor i := 0; i < 3; i++ {\n    err := client.Get(ctx, key).Err()\n    if err == nil { break }\n    if strings.Contains(err.Error(), \"invalid line\") { continue }\n}","handlingStrategy":"retry","validationCode":"// Cannot pre-validate a live stream. Ensure endpoint is real Redis and\n// protocol/TLS matches the server.","typeGuard":null,"tryCatchPattern":"for i := 0; i < 3; i++ {\n    err := client.Get(ctx, key).Err()\n    if err == nil { break }\n    if strings.Contains(err.Error(), \"invalid line\") {\n        // desync — let the pool discard this conn and retry\n        continue\n    }\n    return err\n}","preventionTips":["Connect directly to Redis; avoid RESP-rewriting proxies.","Match TLS settings to the server (rediss:// vs redis://).","Monitor for network corruption and MTU issues."],"tags":["proto","resp","protocol-desync","corruption","network"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}