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

  1. Fix the header template expression — verify every field exists on the campaign model (e.g. {{ .Name }}, {{ .ID }})
  2. Test the template locally with template.Template.ExecuteTemplate against a sample campaign struct
  3. Register any custom template functions used in headers via the manager's template func map
  4. 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

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


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