{"record":{"id":"489d7232ea87c850","repo":"knadh/listmonk","slug":"error-fetching-campaign-subscribers-s-v","errorCode":null,"errorMessage":"error fetching campaign subscribers (%s): %v","messagePattern":"error fetching campaign subscribers \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/manager/pipe.go","lineNumber":85,"sourceCode":"\n\t\tp.cleanup()\n\t}()\n\n\tm.pipesMut.Lock()\n\tm.pipes[c.ID] = p\n\tm.pipesMut.Unlock()\n\treturn p, nil\n}\n\n// NextSubscribers processes the next batch of subscribers in a given campaign.\n// It returns a bool indicating whether any subscribers were processed\n// in the current batch or not. A false indicates that all subscribers\n// have been processed, or that a campaign has been paused or cancelled.\nfunc (p *pipe) NextSubscribers() (bool, error) {\n\t// Fetch the next batch of subscribers from a 'running' campaign.\n\tsubs, err := p.m.store.NextSubscribers(p.camp.ID, p.m.cfg.BatchSize)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"error fetching campaign subscribers (%s): %v\", p.camp.Name, err)\n\t}\n\n\t// There are no subscribers from the query. Either all subscribers on the campaign\n\t// have been processed, or the campaign has changed from 'running' to 'paused' or 'cancelled'.\n\tif len(subs) == 0 {\n\t\treturn false, nil\n\t}\n\n\t// Is there a sliding window limit configured?\n\thasSliding := p.m.cfg.SlidingWindow &&\n\t\tp.m.cfg.SlidingWindowRate > 0 &&\n\t\tp.m.cfg.SlidingWindowDuration.Seconds() > 1\n\n\t// Push messages.\n\tfor _, s := range subs {\n\t\tmsg, err := p.newMessage(s)\n\t\tif err != nil {\n\t\t\tp.m.log.Printf(\"error rendering message (%s) (%s): %v\", p.camp.Name, s.Email, err)","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/manager/pipe.go#L67-L103","documentation":"NextSubscribers pulls the next batch of subscribers for a running campaign from the store. If the store query fails, the error is wrapped with the campaign name and returned; the caller treats this as a batch-fetch failure and stops message dispatch for that iteration.","triggerScenarios":"The store's NextSubscribers query fails — DB connection loss mid-campaign, an invalid custom subscriber query saved on the campaign (failing validation/execution), lock timeouts on the subscribers table, or the underlying query errors due to schema drift.","commonSituations":"Database restarts/failovers while a large campaign is sending, long-running export/lock contention with a custom segment query, DB credential rotation mid-send, or a custom query segment that was edited to invalid SQL after the campaign started.","solutions":["Check database connectivity and error logs for the underlying store error (it's wrapped in the %v)","Re-validate the campaign's custom subscriber query — run validateQueryTables/executable SQL directly against the DB","Retry the send: the pipe stops for this iteration but the campaign can resume once the DB is healthy","Check for schema mismatches after upgrades/migrations and run pending migrations"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Sanity-check the campaign's subscriber query before starting the send\nif err := validateQueryTables(camp.SubscriberQuery); err != nil {\n    return fmt.Errorf(\"invalid subscriber query for campaign %s: %w\", camp.Name, err)\n}","typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < 3; attempt++ {\n    hasMore, err := p.NextSubscribers()\n    if err == nil { break }\n    if !isTransientDBError(err) { return err }\n    time.Sleep(backoff(attempt)) // transient DB outage: retry with backoff\n}","preventionTips":["Monitor DB health during large sends; alert on connection failures","Validate custom subscriber queries at campaign start and after edits","Run pending migrations after upgrades to avoid schema drift errors","Use connection pooling with sane timeouts so failovers recover automatically"],"tags":["database","subscribers","campaign","batch-fetch"],"backgroundTag":"database-query-failed","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}