{"id":"b672ae3e333e53ea","repo":"go-redis/redis","slug":"redis-subscribe-is-not-allowed-on-pooled-connecti","errorCode":null,"errorMessage":"redis: SUBSCRIBE is not allowed on pooled connections when client-side caching is enabled","messagePattern":"redis: SUBSCRIBE is not allowed on pooled connections when client-side caching is enabled","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"csc_integration.go","lineNumber":543,"sourceCode":"\n// errAuthWithCSC rejects runtime authentication because it can change one\n// connection's ACL identity without changing the client's fixed cache namespace.\nvar errAuthWithCSC = errors.New(\n\t\"redis: AUTH is not allowed when client-side caching is enabled\")\n\n// errHelloWithCSC rejects HELLO with arguments because it can switch a tracked\n// connection out of RESP3 (and can also change authentication).\nvar errHelloWithCSC = errors.New(\n\t\"redis: HELLO with arguments is not allowed when client-side caching is enabled\")\n\n// errResetWithCSC rejects RESET because it disables tracking and switches the\n// connection to RESP2.\nvar errResetWithCSC = errors.New(\n\t\"redis: RESET is not allowed when client-side caching is enabled\")\n\n// errSubscribeWithCSC rejects raw subscriptions on the ordinary pool. The\n// typed Subscribe methods use dedicated PubSub connections and remain allowed.\nvar errSubscribeWithCSC = errors.New(\n\t\"redis: SUBSCRIBE is not allowed on pooled connections when client-side caching is enabled\")\n\n// cscCommandError rejects commands that can make a pooled connection's state\n// diverge from the assumptions used by CSC.\nfunc (c *baseClient) cscCommandError(cmd Cmder) error {\n\t// The successful attachment signal is shared with derived clients.\n\t// initConn's internal command wrapper is exempt during library setup.\n\tif !c.cscTrackingRequested() || c.allowClientTracking {\n\t\treturn nil\n\t}\n\tswitch {\n\tcase isClientTrackingCmd(cmd):\n\t\treturn errClientTrackingWithCSC\n\tcase isSelectCmd(cmd):\n\t\treturn errSelectWithCSC\n\tcase isAuthCmd(cmd):\n\t\treturn errAuthWithCSC\n\tcase isProtocolChangingHelloCmd(cmd):","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/csc_integration.go#L525-L561","documentation":"Returned when a raw SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE is sent over an ordinary pooled connection on a CSC-enabled client. Client-side caching requires the pooled connection to stay in RESP3 tracking mode, but raw subscriptions put it into subscribe mode and break that contract. The typed pubsub methods (client.Subscribe) use a dedicated PubSub connection and remain allowed.","triggerScenarios":"Calling client.Do(ctx, \"SUBSCRIBE\", \"channel\") on a client with Options.ClientSideCache set. The cscCommandError guard fires via isSubscribeCmd for any subscribe-family command sent through the normal process path.","commonSituations":"Mixing a hand-rolled subscribe loop with a CSC-enabled client, or copy-pasting subscribe code that uses Do() instead of the typed API.","solutions":["Use the typed pubsub API: pubsub := client.Subscribe(ctx, \"channel\").","If raw subscribe is mandatory, disable built-in CSC (leave Options.ClientSideCache nil)."],"exampleFix":"// before\nclient.Do(ctx, \"SUBSCRIBE\", \"events\")\n// after\npubsub := client.Subscribe(ctx, \"events\")\nmsg, err := pubsub.ReceiveMessage(ctx)","handlingStrategy":"validation","validationCode":"// Always use the typed pubsub API instead of raw SUBSCRIBE.\no := client.Options()\nif o.ClientSideCache != nil || o.ClientSideCacheConfig != nil {\n    pubsub := client.Subscribe(ctx, \"channel\")\n    _ = pubsub\n} else {\n    client.Do(ctx, \"SUBSCRIBE\", \"channel\")\n}","typeGuard":"func canRawSubscribe(c *redis.Client) bool {\n    o := c.Options()\n    return o.ClientSideCache == nil && o.ClientSideCacheConfig == nil\n}","tryCatchPattern":"err := client.Do(ctx, \"SUBSCRIBE\", \"ch\").Err()\nif err != nil && strings.Contains(err.Error(), \"SUBSCRIBE is not allowed\") {\n    pubsub := client.Subscribe(ctx, \"ch\")\n    _ = pubsub.ReceiveMessage(ctx)\n}","preventionTips":["Never use Do(\"SUBSCRIBE\") — always client.Subscribe/Subscribe.","Treat CSC-enabled clients as tracking-only pooled connections."],"tags":["csc","client-side-caching","pubsub","subscribe","command-guard"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}