{"id":"d4b757d4aa447a0b","repo":"go-redis/redis","slug":"redis-zerocopystringcmd-cannot-be-cloned-cmd-wri","errorCode":null,"errorMessage":"redis: ZeroCopyStringCmd cannot be cloned (cmd writes into caller-owned memory)","messagePattern":"redis: ZeroCopyStringCmd cannot be cloned \\(cmd writes into caller-owned memory\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command.go","lineNumber":1098,"sourceCode":"func (cmd *ZeroCopyStringCmd) String() string {\n\tcmd.await()\n\treturn cmdString(cmd, cmd.n)\n}\n\nfunc (cmd *ZeroCopyStringCmd) readReply(rd *proto.Reader) error {\n\t// Reset the byte count before reading so a previous successful run\n\t// can't leak its data through Bytes() if this call errors out before\n\t// updating cmd.n.\n\tcmd.n = 0\n\tif cmd.cloned {\n\t\t// A cloned ZeroCopyStringCmd has no usable destination buffer\n\t\t// (see Clone for the rationale). Drain the network reply so the\n\t\t// connection stays aligned for the next command, then surface a\n\t\t// clear error rather than silently producing a wrong result.\n\t\tif err := rd.DiscardNext(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn fmt.Errorf(\"redis: ZeroCopyStringCmd cannot be cloned (cmd writes into caller-owned memory)\")\n\t}\n\tn, err := rd.ReadStringInto(cmd.buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcmd.n = n\n\treturn nil\n}\n\n// NoRetry returns true because the response is written directly into the\n// caller's buffer. A retry could leave partial data from a failed attempt in\n// the buffer, so the caller must handle retries explicitly if needed.\nfunc (cmd *ZeroCopyStringCmd) NoRetry() bool {\n\treturn true\n}\n\n// Clone returns a clone that is intentionally non-functional. Cloning a\n// ZeroCopyStringCmd has no well-defined semantics: the cmd writes into","sourceCodeStart":1080,"sourceCodeEnd":1116,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/command.go#L1080-L1116","documentation":"Returned by ZeroCopyStringCmd.readReply (command.go:1098) when the cmd instance was produced by Clone(). ZeroCopyStringCmd writes its reply directly into a caller-owned buffer (the buf passed to GetToBuffer); a clone can neither share that buffer safely (concurrent sibling writes would race) nor allocate its own (the result would be invisible to the original caller). Clone therefore returns a marked clone that drains the network reply (to keep the connection aligned) and surfaces this explicit error instead of silently-wrong bytes. In practice the path is unreachable through normal flows: GetToBuffer issues a single-key GET that is never fanned out by cluster routing, and NoRetry()==true prevents the retry path from cloning it.","triggerScenarios":"Theoretically: cluster fan-out routing (osscluster_router.go) attempting to clone a ZeroCopyStringCmd, or a retry path cloning it. Practically unreachable because GetToBuffer is single-key and NoRetry is true. Surfaceable only if custom code forces a Clone on a ZeroCopyStringCmd and then dispatches it.","commonSituations":"Essentially never seen in normal use. Possible in custom cluster-routing experiments, or if a user manually clones pipeline commands and re-executes them.","solutions":["Do not Clone a ZeroCopyStringCmd; if you need a second read, issue a fresh GetToBuffer.","For multi-key/cluster-fanout workloads use the regular Get (StringCmd) which is fully cloneable and retryable.","If using pipelines, remember ZeroCopyStringCmd has NoRetry()==true — handle retries explicitly rather than via the library's automatic clone-and-retry."],"exampleFix":"// before (conceptual — not reachable via normal APIs)\nclone := cmd.(*redis.ZeroCopyStringCmd).Clone()\n_ = client.Process(ctx, clone) // returns the cannot-be-cloned error\n\n// after — issue a fresh zero-copy read\nfresh := client.GetToBuffer(ctx, key, newBuf)","handlingStrategy":"validation","validationCode":"// Never clone a ZeroCopyStringCmd. If you need a second read, issue a fresh GetToBuffer.\n// Use the regular Get (StringCmd) for any path that may clone (cluster fan-out, retry).\nif _, ok := cmd.(*redis.ZeroCopyStringCmd); ok {\n    // do not call Clone(); do not feed into cluster fan-out/retry paths\n}","typeGuard":"func isZeroCopy(cmd redis.Cmder) bool {\n    _, ok := cmd.(*redis.ZeroCopyStringCmd)\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Use regular Get (not GetToBuffer) for multi-key or cluster-fanout workloads.","Remember ZeroCopyStringCmd has NoRetry()==true — handle retries yourself.","Never call Clone() on a ZeroCopyStringCmd; issue a fresh read instead."],"tags":["command","zero-copy","clone","cluster-routing","get"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}