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

  1. Use the default messenger 'email', or omit the messenger field if a default is acceptable.
  2. Check Settings -> SMTP / messengers in the admin UI and re-enable or recreate the referenced messenger.
  3. Re-create the missing SMTP config with the exact same name the campaigns use.
  4. 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

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


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/f88f4cac04c2b0a1. Report an issue: GitHub.