knadh/listmonk · error

error compiling header

Error message

error compiling header %q: %v

What it means

Compilation of a custom campaign header value template failed while preparing the campaign for sending.

Solutions

  1. Fix the template syntax in the header named in the error
  2. Remove template expressions from headers that don't need them (non-templated values are skipped)
  3. Escape literal braces in header values
  4. Test with previewTemplate which compiles headers the same way

Example fix

// before
"X-Custom": "{{ .Name "
// after
"X-Custom": "{{ .Name }}"
Defensive patterns

Strategy: validation

Validate before calling

for _, set := range headers {
    for hdr, val := range set {
        if strings.Contains(val, "{{") {
            if _, err := txttpl.New(hdr).Parse(val); err != nil {
                return fmt.Errorf("invalid template in header %s: %v", hdr, err)
            }
        }
    }
}

Try / catch

if err := c.CompileTemplate(); err != nil {
    if strings.Contains(err.Error(), "error compiling header") {
        // parse the header name from the message and fix that header's value
    }
}

Prevention

When it happens

Trigger: CompileTemplate iterates c.Headers and a header value containing {{ ... }} has invalid syntax or calls an unknown function, e.g. header "X-Campaign": "{{ badFunc .Name }}".

Common situations: Custom mail headers (X-*) templated with subscriber data but containing typos; braces in header values intended as literals; header values copy-pasted with invalid actions.

Related errors


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

Appendix: source

Thrown at models/campaigns.go:234

				c.HeaderTpls = make([]map[string]*txttpl.Template, len(c.Headers))
				break
			}
		}
		if c.HeaderTpls != nil {
			break
		}
	}
	if c.HeaderTpls != nil {
		var txtFuncs map[string]any = f
		for i, set := range c.Headers {
			c.HeaderTpls[i] = make(map[string]*txttpl.Template, len(set))
			for hdr, val := range set {
				if !hasTplExpr(val) {
					continue
				}
				tpl, err := txttpl.New(ContentTpl).Funcs(txtFuncs).Parse(val)
				if err != nil {
					return fmt.Errorf("error compiling header %q: %v", hdr, err)
				}
				c.HeaderTpls[i][hdr] = tpl
			}
		}
	}

	return nil
}

// hasTplExpr checks whether a given string has a Go template expression with {{ and  }}.
func hasTplExpr(s string) bool {
	_, after, ok := strings.Cut(s, "{{")
	return ok && strings.Contains(after, "}}")
}

// ConvertContent converts a campaign's body from one format to another,
// for example, Markdown to HTML.
func (c *Campaign) ConvertContent(from, to string) (string, error) {

View on GitHub (pinned to 670c01717d)