{"record":{"id":"d2134da58bfbb523","repo":"nats-io/nats-server","slug":"gsl-notification-already-registered","errorCode":null,"errorMessage":"gsl: notification already registered","messagePattern":"gsl: notification already registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/gsl/gsl.go","lineNumber":43,"sourceCode":"// match multiple published subjects.\n\n// Common byte variables for wildcards and token separator.\nconst (\n\tpwc     = '*'\n\tpwcs    = \"*\"\n\tfwc     = '>'\n\tfwcs    = \">\"\n\ttsep    = \".\"\n\tbtsep   = '.'\n\t_EMPTY_ = \"\"\n)\n\n// Sublist related errors\nvar (\n\tErrInvalidSubject    = errors.New(\"gsl: invalid subject\")\n\tErrNotFound          = errors.New(\"gsl: no matches found\")\n\tErrNilChan           = errors.New(\"gsl: nil channel\")\n\tErrAlreadyRegistered = errors.New(\"gsl: notification already registered\")\n)\n\n// SimpleSublist is an alias type for GenericSublist that takes\n// empty values, useful for tracking interest only without any\n// unnecessary allocations.\ntype SimpleSublist = GenericSublist[struct{}]\n\n// NewSimpleSublist will create a simple sublist.\nfunc NewSimpleSublist() *SimpleSublist {\n\treturn &GenericSublist[struct{}]{root: newLevel[struct{}]()}\n}\n\n// A GenericSublist stores and efficiently retrieves subscriptions.\ntype GenericSublist[T comparable] struct {\n\tsync.RWMutex\n\troot  *level[T]\n\tcount uint32\n}","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/gsl/gsl.go#L25-L61","documentation":"This error is returned by the GSL (generic sublist) notification registry when the same notification channel is registered more than once for the same subject subscription interest. addNotify scans the existing channel list and returns ErrAlreadyRegistered if the exact channel is already present. It is a duplicate-registration guard, not a network or persistence failure.","triggerScenarios":"Calling addNotify (via sublist notification registration APIs) on a GenericSublist where the same channel instance is already registered for the subject; the equality check in the for-range loop over existing channels matches and returns ErrAlreadyRegistered.","commonSituations":"Re-initializing a server component without unregistering the previous notifier; double-subscribe logic in wrappers that call RegisterNotification twice with the same channel; retrying a registration call after a timeout without removing the prior channel.","solutions":["Check whether the channel is already registered before calling the registration API, or treat ErrAlreadyRegistered as idempotent success.","Unregister the existing notification channel (removeNotify) before registering it again.","Create a fresh channel for each registration instead of reusing one channel instance.","Refactor the caller so registration happens exactly once per lifecycle (e.g. guard with sync.Once)."],"exampleFix":"// before\nch := make(chan struct{}, 1)\nsl.RegisterNotification(sub, ch)\nsl.RegisterNotification(sub, ch) // gsl: notification already registered\n// after\nch := make(chan struct{}, 1)\nif err := sl.RegisterNotification(sub, ch); err != nil && !errors.Is(err, gsl.ErrAlreadyRegistered) {\n    return err\n}","handlingStrategy":"validation","validationCode":"func alreadyRegistered(chs []chan struct{}, ch chan struct{}) bool {\n    for _, c := range chs {\n        if c == ch { return true }\n    }\n    return false\n}\n// call register only if !alreadyRegistered(...)\n","typeGuard":"func isAlreadyRegistered(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"notification already registered\")\n}","tryCatchPattern":"if err := sl.RegisterNotification(sub, ch); err != nil && !isAlreadyRegistered(err) {\n    return fmt.Errorf(\"register notification: %w\", err)\n}\n// idempotent: ignore duplicate registration","preventionTips":["Register each notification channel exactly once per subject (guard with sync.Once or a set)","Unregister the channel before re-registering","Treat ErrAlreadyRegistered as idempotent success in wrapper APIs","Use a fresh channel per registration instead of reusing instances"],"tags":["golang","nats-server","sublist","duplicate-registration"],"backgroundTag":"duplicate-registration","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}