knadh/listmonk · error
error rendering header %q: %v
Error message
error rendering header %q: %v
What it means
Campaign message headers may themselves be templates (e.g. 'X-Campaign: {{ .Name }}'). During render, each templated header value is executed with the campaign as data; if template execution fails (bad template syntax, unknown function/field, wrong data type), this wrapped error names the offending header and the underlying template error.
Source
Thrown at internal/manager/message.go:80
}
// If there are templated headers, compile them.
if m.Campaign.HeaderTpls == nil {
m.headers = m.Campaign.Headers
} else {
hdrOut := bytes.Buffer{}
m.headers = make(models.Headers, len(m.Campaign.Headers))
for i, set := range m.Campaign.Headers {
m.headers[i] = make(map[string]string, len(set))
for hdr, val := range set {
tpl := m.Campaign.HeaderTpls[i][hdr]
if tpl == nil {
m.headers[i][hdr] = val
continue
}
hdrOut.Reset()
if err := tpl.ExecuteTemplate(&hdrOut, models.ContentTpl, m); err != nil {
return fmt.Errorf("error rendering header %q: %v", hdr, err)
}
m.headers[i][hdr] = hdrOut.String()
}
}
}
return nil
}
// Subject returns a copy of the message subject
func (m *CampaignMessage) Subject() string {
return m.subject
}
// Body returns a copy of the message body.
func (m *CampaignMessage) Body() []byte {
out := make([]byte, len(m.body))
copy(out, m.body)View on GitHub (pinned to 670c01717d)
Solutions
- Fix the header template expression — verify every field exists on the campaign model (e.g. {{ .Name }}, {{ .ID }})
- Test the template locally with template.Template.ExecuteTemplate against a sample campaign struct
- Register any custom template functions used in headers via the manager's template func map
- Simplify or remove the templating from the header if it's static content
Example fix
// before
"X-Campaign": "{{ .CampagnName }}" // typo -> can't evaluate field
// after
"X-Campaign": "{{ .Name }}" Defensive patterns
Strategy: try-catch
Validate before calling
// Validate header templates at config/campaign-save time
for _, v := range headers {
t, err := template.New("hdr").Funcs(funcMap).Parse(v)
if err != nil { return fmt.Errorf("bad header template %q: %v", v, err) }
if err := t.Execute(io.Discard, sampleCampaign); err != nil { return err }
} Try / catch
if err := renderHeaders(m); err != nil {
var hdr string
if matches := hdrErrRe.FindStringSubmatch(err.Error()); matches != nil {
hdr = matches[1] // header name from 'error rendering header %q'
}
return fmt.Errorf("fix templated header %q: %w", hdr, err)
} Prevention
- Pre-validate header templates with a sample campaign at campaign save time
- Only reference fields that exist on the campaign model (Name, ID, etc.)
- Register every custom template function used in headers in the manager's func map
- Keep header templates simple; avoid nested/typed field access in header expressions
When it happens
Trigger: Defining a campaign header value containing invalid Go template syntax ({{ .Nonexistent.Depth }}), calling an undefined template function, or a field type mismatch in the header expression while NewCampaignMessage renders the message.
Common situations: Typos in header template expressions like {{ .CampagnName }}, custom template funcs not registered in the manager, copy-pasted header templates from examples with unsupported fields, or whitespace/pipe errors in the template.
Related errors
- campaigns.fieldInvalidBody
- error compiling header %q: %v
- campaigns.fieldInvalidSubject
- campaigns.fieldInvalidName
- template %d not found
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/ceb5b50844af0fac.
Report an issue: GitHub.