{"record":{"id":"a6b8400fdfef5f7c","repo":"redis/go-redis","slug":"csc-a-different-invalidate-push-handler-is-alre","errorCode":null,"errorMessage":"csc: a different \"invalidate\" push handler is already registered","messagePattern":"csc: a different \"invalidate\" push handler is already registered","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"csc_integration.go","lineNumber":156,"sourceCode":"\treturn typ == reflect.TypeOf(b) && typ.Comparable() && a == b\n}\n\nfunc isNilCache(cache Cache) bool {\n\tif cache == nil {\n\t\treturn true\n\t}\n\tv := reflect.ValueOf(cache)\n\tswitch v.Kind() {\n\tcase reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:\n\t\treturn v.IsNil()\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// errInvalidateHandlerBound: piggybacking on a handler bound to a live\n// different cache would leave the new cache uninvalidated.\nvar errInvalidateHandlerBound = errors.New(`csc: a different \"invalidate\" push handler is already registered`)\n\n// bindTo binds the handler to (cache, keyPrefix). Success when that is already the\n// binding (a derived Client.Conn sharing the parent's processor and cache) or\n// when the handler was released by a previous owner's teardown (rebind);\n// errInvalidateHandlerBound otherwise.\nfunc (h *invalidateHandler) bindTo(cache Cache, keyPrefix string) error {\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\tswitch {\n\tcase sameCache(h.cache, cache) && h.keyPrefix == keyPrefix:\n\t\th.users++\n\t\treturn nil\n\tcase h.cache == nil:\n\t\th.cache, h.keyPrefix = cache, keyPrefix\n\t\th.users = 1\n\t\treturn nil\n\tdefault:\n\t\treturn errInvalidateHandlerBound","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/csc_integration.go#L138-L174","documentation":"go-redis's built-in client-side caching registers a RESP3 'invalidate' push handler on the connection. Only one handler can be bound, and it must be bound to the same cache; errInvalidateHandlerBound (returned by bindTo/registerInvalidateHandler) is returned when a second cache tries to piggyback on a handler already bound to a different live cache, because the new cache would never be invalidated.","triggerScenarios":"Creating two caches with built-in CSC sharing one underlying connection/processor and binding both; deriving a second, different cache from a shared parent client's Conn (Client.Conn()) without releasing the first.","commonSituations":"Sharing a single redis.Client across multiple application caches; leaking a first cache without teardown then creating another on the same connection.","solutions":["Use one cache per shared client/connection, or create a separate redis.Client (own connection) for each cache.","Ensure the previous cache is torn down/released before binding a new one to the same connection.","If caches genuinely share the same cache instance and keyPrefix, bindTo succeeds idempotently — verify you are not accidentally constructing a second cache object."],"exampleFix":"// before\ncache1 := redis.NewClientCache(client, ...)\ncache2 := redis.NewClientCache(client, ...) // same conn, different cache -> error\n// after\ncache1 := redis.NewClientCache(client, ...)\nclient2 := redis.NewClient(clientOpts) // separate connection\ncache2 := redis.NewClientCache(client2, ...)","handlingStrategy":"try-catch","validationCode":"// ensure only one cache binds per connection before creating caches\n// (track bindings in a registry keyed by client)\nif alreadyBound(client) && cacheFor(client) != newCache {\n    return errInvalidateHandlerBound\n}","typeGuard":null,"tryCatchPattern":"if err := cache.bindTo(c, prefix); err != nil {\n    if errors.Is(err, errInvalidateHandlerBound) {\n        // use a dedicated client/connection for this cache\n    }\n}","preventionTips":["One cache per client/connection; give each cache its own redis.Client.","Tear down caches before creating replacements on the same connection.","Avoid deriving multiple caches from a single Client.Conn()."],"tags":["client-side-caching","push-handler","configuration"],"backgroundTag":"invalidate-handler-conflict","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}