knadh/listmonk · error
campaigns.fieldInvalidSubject
Error message
campaigns.fieldInvalidSubject
What it means
validateCampaignFields checks strHasLen(c.Subject, 1, 5000). The subject must be non-empty and can hold up to 5000 characters because Go template logic ({{ ... }}) is allowed inside it. This error means the subject is missing or exceeds that limit.
Source
Thrown at cmd/campaigns.go:695
}
// validateCampaignFields validates incoming campaign field values.
func (a *App) validateCampaignFields(c campReq) (campReq, error) {
if c.FromEmail == "" {
c.FromEmail = a.cfg.FromEmail
} else if !reFromAddress.Match([]byte(c.FromEmail)) {
if _, err := a.importer.SanitizeEmail(c.FromEmail); err != nil {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidFromEmail"))
}
}
if !strHasLen(c.Name, 1, stdInputMaxLen) {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidName"))
}
// Larger char limit for subject as it can contain {{ go templating }} logic.
if !strHasLen(c.Subject, 1, 5000) {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidSubject"))
}
// If no content-type is specified, default to richtext.
if c.ContentType != models.CampaignContentTypeRichtext &&
c.ContentType != models.CampaignContentTypeHTML &&
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()) {View on GitHub (pinned to 670c01717d)
Solutions
- Set a non-empty subject in the campaign request payload.
- Keep template logic in the body/template and use a short subject with small placeholders like {{ .Subscriber.Name }}.
- Pre-measure subject length client-side (limit 5000) before submitting.
Example fix
// before
{"subject": ""}
// after
{"subject": "Your weekly digest, {{ .Subscriber.FirstName }}"} Defensive patterns
Strategy: validation
Validate before calling
function validateSubject(subject) {
if (typeof subject !== 'string' || subject.trim().length === 0 || subject.length > 5000) {
throw new Error('subject must be 1-5000 characters (template logic included)');
}
} Type guard
function hasValidSubject(v: unknown): v is string {
return typeof v === 'string' && v.length >= 1 && v.length <= 5000;
} Try / catch
try {
await api.createCampaign({ subject, ...rest });
} catch (e) {
if (e.message.includes('fieldInvalidSubject')) {
throw new Error('Subject is required and must be at most 5000 characters');
}
throw e;
} Prevention
- Keep complex template logic in the body, not the subject.
- Check subject length (including template text) before submit.
- Never allow empty subjects from form submissions.
When it happens
Trigger: CreateCampaign/UpdateCampaign/TestCampaign with an empty 'subject' field, or a subject over 5000 chars — e.g. a subject embedding very large template blocks or long dynamic text.
Common situations: Programmatic campaign creation where the subject is optional in the caller's schema but required here; subjects with big conditional template snippets blowing past 5000 chars.
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
- campaigns.fieldInvalidBody
- campaigns.fieldInvalidFromEmail
- campaigns.fieldInvalidName
- campaigns.fieldInvalidSendAt
- campaigns.fieldInvalidListIDs
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/9cacaf5fe8d857ad.
Report an issue: GitHub.