knadh/listmonk · error
campaigns.fieldInvalidMessenger
Error message
campaigns.fieldInvalidMessenger
What it means
After checking a messenger fallback (only 'email-*' prefixed ones degrade to 'email'), validateCampaignFields rejects a messenger the manager doesn't have: if !a.manager.HasMessenger(c.Messenger) it returns fieldInvalidMessenger with the messenger name interpolated. It means the requested delivery channel (SMTP config, webhook, etc.) is not registered/available.
Source
Thrown at cmd/campaigns.go:727
}
// If there's a "send_at" date, it should be in the future.
if c.SendAt.Valid {
if c.SendAt.Time.Before(time.Now()) {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidSendAt"))
}
}
if len(c.ListIDs) == 0 {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidListIDs"))
}
if !a.manager.HasMessenger(c.Messenger) {
// If it's a specific SMTP, but it's no longer available (removed/disabled), fall back to general email messenger.
if strings.HasPrefix(c.Messenger, "email-") {
c.Messenger = "email"
} else {
return c, errors.New(a.i18n.Ts("campaigns.fieldInvalidMessenger", "name", c.Messenger))
}
}
camp := models.Campaign{Body: c.Body, TemplateBody: tplTag}
if err := c.CompileTemplate(a.manager.TemplateFuncs(&camp)); err != nil {
return c, errors.New(a.i18n.Ts("campaigns.fieldInvalidBody", "error", err.Error()))
}
if len(c.Headers) == 0 {
c.Headers = make([]map[string]string, 0)
}
// Validate and initialize attribs.
if c.Attribs != nil {
if _, err := json.Marshal(c.Attribs); err != nil {
return c, errors.New(a.i18n.T("subscribers.invalidJSON"))
}
}View on GitHub (pinned to 670c01717d)
Solutions
- Use the default messenger 'email', or omit the messenger field if a default is acceptable.
- Check Settings -> SMTP / messengers in the admin UI and re-enable or recreate the referenced messenger.
- Re-create the missing SMTP config with the exact same name the campaigns use.
- List available messengers (GET /api/settings) and pick one that exists.
Example fix
// before
{"messenger": "email-old-smtp"}
// after
{"messenger": "email"} Defensive patterns
Strategy: fallback
Validate before calling
// fetch available messengers first, then pick a valid one const settings = await api.getSettings(); const messenger = settings.messengers?.includes(desired) ? desired : 'email';
Type guard
function isKnownMessenger(v: unknown, available: string[]): v is string {
return typeof v === 'string' && (v === 'email' || available.includes(v));
} Try / catch
try {
await api.createCampaign({ messenger, ...rest });
} catch (e) {
if (e.message.includes('fieldInvalidMessenger')) {
// retry with the default messenger
await api.updateCampaign(id, { messenger: 'email' });
} else { throw e; }
} Prevention
- Keep messenger names in configuration, not hard-coded in scripts.
- After deleting/renaming an SMTP config, update or recreate campaigns referencing it.
- Validate the messenger against GET /api/settings before submission.
- Use the plain 'email' messenger when no special channel is needed.
When it happens
Trigger: CreateCampaign/UpdateCampaign/TestCampaign with a messenger value that no enabled messenger provides, e.g. a named SMTP ('email-smtp2') that was deleted or disabled, or a typo like 'emial'.
Common situations: Admin disabled or renamed an SMTP config after campaigns referenced it; messenger defined only in settings but the corresponding service isn't started; environment drift between staging and production messenger names.
Related errors
- unknown messenger %s on campaign %s
- campaigns.fieldInvalidFromEmail
- campaigns.fieldInvalidName
- campaigns.fieldInvalidSubject
- campaigns.fieldInvalidSendAt
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/f88f4cac04c2b0a1.
Report an issue: GitHub.