knadh/listmonk · error

campaigns.fieldInvalidListIDs

Error message

campaigns.fieldInvalidListIDs

What it means

validateCampaignFields requires at least one target list: if len(c.ListIDs) == 0 it returns campaigns.fieldInvalidListIDs. A campaign is only meaningful when addressed to subscriber lists, so the empty case is rejected.

Source

Thrown at cmd/campaigns.go:719

		c.ContentType != models.CampaignContentTypePlain &&
		c.ContentType != models.CampaignContentTypeVisual &&
		c.ContentType != models.CampaignContentTypeMarkdown {
		c.ContentType = models.CampaignContentTypeRichtext
	}

	if c.ContentType != models.CampaignContentTypeVisual {
		c.BodySource.Valid = false
	}

	// 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)

View on GitHub (pinned to 670c01717d)

Solutions

  1. Include at least one valid list ID in the lists array of the request.
  2. Create a list first (POST /api/lists) if none exists, then reference its ID.
  3. In the UI, disable submit until a list is selected; validate client-side.
  4. Verify list IDs are integers and that the array is non-null.

Example fix

// before
{"name": "News", "lists": []}
// after
{"name": "News", "lists": [1, 3]}
Defensive patterns

Strategy: validation

Validate before calling

function requireLists(listIds) {
  if (!Array.isArray(listIds) || listIds.length === 0) {
    throw new Error('at least one list id is required');
  }
  if (!listIds.every(n => Number.isInteger(n) && n > 0)) {
    throw new Error('all list ids must be positive integers');
  }
}

Type guard

function hasListIds(v: unknown): v is number[] {
  return Array.isArray(v) && v.length > 0 && v.every((n): n is number => Number.isInteger(n));
}

Try / catch

try {
  await api.createCampaign({ lists: listIds, ...rest });
} catch (e) {
  if (e.message.includes('fieldInvalidListIDs')) {
    throw new Error('Select at least one subscriber list before creating the campaign');
  }
  throw e;
}

Prevention

When it happens

Trigger: CreateCampaign, UpdateCampaign, or TestCampaign with 'lists' (ListIDs) missing, empty array, or null in the request body.

Common situations: Front-end allowing campaign creation before the user selects recipients; automation scripts copying payloads that had lists stripped; sending list IDs as strings instead of integers and ending up with an empty parsed array.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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