knadh/listmonk · error
error fetching campaign subscribers (%s): %v
Error message
error fetching campaign subscribers (%s): %v
What it means
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.
Source
Thrown at internal/manager/pipe.go:85
p.cleanup()
}()
m.pipesMut.Lock()
m.pipes[c.ID] = p
m.pipesMut.Unlock()
return p, nil
}
// NextSubscribers processes the next batch of subscribers in a given campaign.
// It returns a bool indicating whether any subscribers were processed
// in the current batch or not. A false indicates that all subscribers
// have been processed, or that a campaign has been paused or cancelled.
func (p *pipe) NextSubscribers() (bool, error) {
// Fetch the next batch of subscribers from a 'running' campaign.
subs, err := p.m.store.NextSubscribers(p.camp.ID, p.m.cfg.BatchSize)
if err != nil {
return false, fmt.Errorf("error fetching campaign subscribers (%s): %v", p.camp.Name, err)
}
// There are no subscribers from the query. Either all subscribers on the campaign
// have been processed, or the campaign has changed from 'running' to 'paused' or 'cancelled'.
if len(subs) == 0 {
return false, nil
}
// Is there a sliding window limit configured?
hasSliding := p.m.cfg.SlidingWindow &&
p.m.cfg.SlidingWindowRate > 0 &&
p.m.cfg.SlidingWindowDuration.Seconds() > 1
// Push messages.
for _, s := range subs {
msg, err := p.newMessage(s)
if err != nil {
p.m.log.Printf("error rendering message (%s) (%s): %v", p.camp.Name, s.Email, err)View on GitHub (pinned to 670c01717d)
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
Defensive patterns
Strategy: retry
Validate before calling
// Sanity-check the campaign's subscriber query before starting the send
if err := validateQueryTables(camp.SubscriberQuery); err != nil {
return fmt.Errorf("invalid subscriber query for campaign %s: %w", camp.Name, err)
} Try / catch
for attempt := 0; attempt < 3; attempt++ {
hasMore, err := p.NextSubscribers()
if err == nil { break }
if !isTransientDBError(err) { return err }
time.Sleep(backoff(attempt)) // transient DB outage: retry with backoff
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- campaigns.fieldInvalidFromEmail
- campaigns.fieldInvalidName
- campaigns.fieldInvalidSubject
- campaigns.fieldInvalidSendAt
- campaigns.fieldInvalidListIDs
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/489d7232ea87c850.
Report an issue: GitHub.