{"record":{"id":"9cacaf5fe8d857ad","repo":"knadh/listmonk","slug":"campaigns-fieldinvalidsubject","errorCode":null,"errorMessage":"campaigns.fieldInvalidSubject","messagePattern":"campaigns\\.fieldInvalidSubject","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/campaigns.go","lineNumber":695,"sourceCode":"}\n\n// validateCampaignFields validates incoming campaign field values.\nfunc (a *App) validateCampaignFields(c campReq) (campReq, error) {\n\tif c.FromEmail == \"\" {\n\t\tc.FromEmail = a.cfg.FromEmail\n\t} else if !reFromAddress.Match([]byte(c.FromEmail)) {\n\t\tif _, err := a.importer.SanitizeEmail(c.FromEmail); err != nil {\n\t\t\treturn c, errors.New(a.i18n.T(\"campaigns.fieldInvalidFromEmail\"))\n\t\t}\n\t}\n\n\tif !strHasLen(c.Name, 1, stdInputMaxLen) {\n\t\treturn c, errors.New(a.i18n.T(\"campaigns.fieldInvalidName\"))\n\t}\n\n\t// Larger char limit for subject as it can contain {{ go templating }} logic.\n\tif !strHasLen(c.Subject, 1, 5000) {\n\t\treturn c, errors.New(a.i18n.T(\"campaigns.fieldInvalidSubject\"))\n\t}\n\n\t// If no content-type is specified, default to richtext.\n\tif c.ContentType != models.CampaignContentTypeRichtext &&\n\t\tc.ContentType != models.CampaignContentTypeHTML &&\n\t\tc.ContentType != models.CampaignContentTypePlain &&\n\t\tc.ContentType != models.CampaignContentTypeVisual &&\n\t\tc.ContentType != models.CampaignContentTypeMarkdown {\n\t\tc.ContentType = models.CampaignContentTypeRichtext\n\t}\n\n\tif c.ContentType != models.CampaignContentTypeVisual {\n\t\tc.BodySource.Valid = false\n\t}\n\n\t// If there's a \"send_at\" date, it should be in the future.\n\tif c.SendAt.Valid {\n\t\tif c.SendAt.Time.Before(time.Now()) {","sourceCodeStart":677,"sourceCodeEnd":713,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/cmd/campaigns.go#L677-L713","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"subject\": \"\"}\n// after\n{\"subject\": \"Your weekly digest, {{ .Subscriber.FirstName }}\"}","handlingStrategy":"validation","validationCode":"function validateSubject(subject) {\n  if (typeof subject !== 'string' || subject.trim().length === 0 || subject.length > 5000) {\n    throw new Error('subject must be 1-5000 characters (template logic included)');\n  }\n}","typeGuard":"function hasValidSubject(v: unknown): v is string {\n  return typeof v === 'string' && v.length >= 1 && v.length <= 5000;\n}","tryCatchPattern":"try {\n  await api.createCampaign({ subject, ...rest });\n} catch (e) {\n  if (e.message.includes('fieldInvalidSubject')) {\n    throw new Error('Subject is required and must be at most 5000 characters');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["validation","campaign","template"],"backgroundTag":"missing-required-argument","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}