{"record":{"id":"7720a160b362236b","repo":"redis/go-redis","slug":"redis-can-t-parse-verbatim-string-reply-q","errorCode":null,"errorMessage":"redis: can't parse verbatim string reply: %q","messagePattern":"redis: can't parse verbatim string reply: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":421,"sourceCode":"\t\treturn \"\", err\n\t}\n\n\tb := make([]byte, n+2)\n\t_, err = io.ReadFull(r.rd, b)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn util.BytesToString(b[:n]), nil\n}\n\nfunc (r *Reader) readVerb(line []byte) (string, error) {\n\ts, err := r.readStringReply(line)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(s) < 4 || s[3] != ':' {\n\t\treturn \"\", fmt.Errorf(\"redis: can't parse verbatim string reply: %q\", line)\n\t}\n\treturn s[4:], nil\n}\n\nfunc (r *Reader) readSlice(line []byte) ([]interface{}, error) {\n\tn, err := replyLen(line)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tval := make([]interface{}, n)\n\tfor i := 0; i < len(val); i++ {\n\t\tv, err := r.ReadReply()\n\t\tif err != nil {\n\t\t\tif err == Nil {\n\t\t\t\tval[i] = nil\n\t\t\t\tcontinue\n\t\t\t}","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/internal/proto/reader.go#L403-L439","documentation":"This error is thrown when go-redis receives a RESP3 verbatim string reply (the '=' reply type, e.g. from LODEN/? commands like INFO with verbatim output) whose payload does not have the required 4-byte type prefix ('txt:' or 'mkd:') followed by the actual content. The protocol expects the first 3 characters to be a format identifier and the 4th character to be a colon. If the payload is shorter than 4 bytes or byte 3 is not ':', the reply is malformed for the verbatim-string contract and the library refuses to guess the format.","triggerScenarios":"Calling any command that returns a RESP3 verbatim string ('=' reply) — such as DEBUG SLEEP, DEBUG JMAP, or module commands emitting verbatim strings — while the server returns a verbatim payload without the 'txt:'/'mkd:' prefix. Also triggered when a non-Redis or proxy server (e.g. a RESP proxy or a Twemproxy/envoy layer) replies with a bare bulk-like string using the '=' type instead of '$'. ReadReply() and ReadString() funnel through readVerb, so any of those surfaces the error.","commonSituations":"Talking to a RESP proxy or middleware that misformats verbatim replies; connecting to a Redis-compatible server (e.g. KeyDB fork quirks or an embedded/embedded-in-memory RESP implementation) that emits '=' replies without the type prefix; running with Protocol: 3 against a server that only partially implements RESP3; calling commands that shouldn't return verbatim strings against the wrong endpoint (e.g. sending an admin/debug command to a load balancer).","solutions":["Check what command produced the reply; ensure the target actually supports that command and RESP3 verbatim strings (run with Protocol: 3 only against servers that support RESP3).","Remove or bypass any RESP-rewriting proxy between the client and the Redis server, or upgrade it to a version that forwards '=' replies with the type prefix intact.","If you control the server/module emitting the reply, make it prefix verbatim payloads with 'txt:' (or another valid 3-char format + ':').","As a workaround, switch the connection to Protocol: 2 so the reply is delivered as a plain bulk string instead of a verbatim string, or parse the raw reply yourself via a custom hook."],"exampleFix":"// before: forcing RESP3 against a proxy that mangles verbatim replies\nopts := &redis.Options{Addr: \"proxy:6379\", Protocol: 3}\n// after: use RESP2 so verbatim '=' replies arrive as plain bulk strings\nopts := &redis.Options{Addr: \"proxy:6379\", Protocol: 2}","handlingStrategy":"try-catch","validationCode":"// Pre-check: only issue verbatim-returning commands against servers confirmed to\n// support RESP3 verbatim strings, and pin the protocol explicitly.\nfunc verbatimSafe(opts *redis.Options) bool {\n    return opts.Protocol == 3 && !behindRewritingProxy(opts.Addr) // your own check\n}","typeGuard":"func isVerbatimParseError(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"redis: can't parse verbatim string reply:\")\n}","tryCatchPattern":"res, err := client.Do(ctx, \"DEBUG\", \"JMAP\", key).Result()\nif err != nil {\n    if isVerbatimParseError(err) {\n        // fallback: re-issue with RESP2 semantics or parse raw string yourself\n        res, err = fallbackPlainString(ctx, client, key)\n    }\n    if err != nil { return err }\n}","preventionTips":["Run Protocol: 3 only against servers that fully implement RESP3 verbatim strings.","Avoid putting reply-rewriting proxies (old Twemproxy/envoy configs) in front of verbatim-returning commands.","Test module/debug commands against the exact server version and proxy chain used in production.","Wrap raw Do() calls for exotic commands in a helper that maps parse errors to a domain error with the command name."],"tags":["go","redis","resp3","protocol-parsing"],"backgroundTag":"resp-protocol-parse-error","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}