{"record":{"id":"0ea325b639ff9963","repo":"knadh/listmonk","slug":"messenger-s-is-already-loaded","errorCode":null,"errorMessage":"messenger '%s' is already loaded","messagePattern":"messenger '(.+?)' is already loaded","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/manager/manager.go","lineNumber":203,"sourceCode":"\t\tmessengers:   make(map[string]Messenger),\n\t\tpipes:        make(map[int]*pipe),\n\t\ttpls:         make(map[int]*models.Template),\n\t\tlinks:        make(map[string]string),\n\t\tnextPipes:    make(chan *pipe, 1000),\n\t\tcampMsgQ:     make(chan CampaignMessage, cfg.Concurrency*cfg.MessageRate*2),\n\t\tmsgQ:         make(chan models.Message, cfg.Concurrency*cfg.MessageRate*2),\n\t\tslidingStart: time.Now(),\n\t}\n\tm.tplFuncs = m.makeGnericFuncMap()\n\n\treturn m\n}\n\n// AddMessenger adds a Messenger messaging backend to the manager.\nfunc (m *Manager) AddMessenger(msg Messenger) error {\n\tid := msg.Name()\n\tif _, ok := m.messengers[id]; ok {\n\t\treturn fmt.Errorf(\"messenger '%s' is already loaded\", id)\n\t}\n\tm.messengers[id] = msg\n\n\treturn nil\n}\n\n// PushMessage pushes an arbitrary non-campaign Message to be sent out by the workers.\n// It times out if the queue is busy.\nfunc (m *Manager) PushMessage(msg models.Message) error {\n\tt := time.NewTicker(pushTimeout)\n\tdefer t.Stop()\n\n\tselect {\n\tcase m.msgQ <- msg:\n\tcase <-t.C:\n\t\tm.log.Printf(\"message push timed out: '%s'\", msg.Subject)\n\t\treturn errors.New(\"message push timed out\")\n\t}","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/manager/manager.go#L185-L221","documentation":"Manager.AddMessenger registers a messaging backend (email/SMS provider) by the name returned by msg.Name(). If a messenger with that name is already registered in the manager, the second registration is rejected with this error to keep the messenger map unambiguous.","triggerScenarios":"Calling AddMessenger twice with two Messenger implementations whose Name() returns the same string, or re-initializing the app (e.g. in tests or hot-reload) without creating a fresh Manager.","commonSituations":"Registering both 'smtp' and a custom sender that forgot to override Name() (empty or default name), initializing the manager in a loop during config reloads, or test suites adding the same messenger across subtests on a shared manager.","solutions":["Give each Messenger implementation a unique, explicit Name() return value","Create a new Manager instance instead of reusing one across re-initializations","Check before adding: if the messenger already exists, replace or skip it deliberately (remove and re-add if replacement is intended)","Fix empty/duplicate names from config-driven messenger factories"],"exampleFix":"// before\nfunc (s *MySender) Name() string { return \"\" } // collides\n// after\nfunc (s *MySender) Name() string { return \"my-sender\" }","handlingStrategy":"try-catch","validationCode":"// Pre-check uniqueness of messenger names before registration\nnames := map[string]bool{}\nfor _, msgr := range messengers {\n    if names[msgr.Name()] { return fmt.Errorf(\"duplicate messenger name: %s\", msgr.Name()) }\n    names[msgr.Name()] = true\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.AddMessenger(msgr); err != nil {\n    if strings.Contains(err.Error(), \"is already loaded\") {\n        log.Printf(\"messenger %q already registered, skipping\", msgr.Name())\n        return nil\n    }\n    return err\n}","preventionTips":["Give every Messenger a unique, explicit Name() return value","Construct a fresh Manager per app lifecycle instead of re-adding messengers to a shared one","In tests, create a new Manager per test or dedupe by name before adding","Never leave Name() returning \"\" or a package-level default for multiple implementations"],"tags":["messenger","registration","duplicate","initialization"],"backgroundTag":"duplicate-registration","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}