amir20/dozzle · error

failed to parse template

Error message

failed to parse template: %w

What it means

When a non-empty templateStr is supplied, NewWebhookDispatcher parses it with text/template at construction time. A syntactically invalid template (unclosed actions, bad pipeline syntax) aborts dispatcher creation with this wrapped error.

Solutions

  1. Check the wrapped parse error; it names the template and character position
  2. Ensure every {{ }} action is closed and pipelines are valid Go template syntax
  3. Test the template standalone with template.New("webhook").Parse before saving the config
  4. If you just want raw JSON of the notification, pass an empty templateStr instead

Example fix

// before
NewWebhookDispatcher("hook", url, "{\"text\": \"{{ .Message \"}", nil)
// after
NewWebhookDispatcher("hook", url, "{\"text\": \"{{ .Message }}\"}", nil)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := template.New("webhook").Parse(tpl); err != nil {
    return fmt.Errorf("invalid webhook template: %w", err)
}

Try / catch

d, err := NewWebhookDispatcher(name, rawURL, tpl, headers)
if err != nil && strings.Contains(err.Error(), "failed to parse template") {
    // show template syntax error to user
}

Prevention

When it happens

Trigger: Calling NewWebhookDispatcher with templateStr that text/template cannot parse, e.g. "{{ .Message" (missing closing braces) or "{{ .Field | }" (empty pipeline).

Common situations: Hand-written JSON templates with typos in {{ }} actions; templates copied from other engines (Jinja2 {{ }}, ${var} syntax) that Go templates reject; YAML config mangling braces.

Understand the failure class

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/19003611a25ca1d0. Report an issue: GitHub.

Appendix: source

Thrown at internal/notification/dispatcher/webhook.go:190

		Name:         name,
		URL:          rawURL,
		TemplateText: templateStr,
		Headers:      headers,
		client: &http.Client{
			Timeout: 10 * time.Second,
			Transport: &http.Transport{
				DialContext:           safeDialContext,
				TLSHandshakeTimeout:   10 * time.Second,
				ResponseHeaderTimeout: 10 * time.Second,
				ExpectContinueTimeout: 1 * time.Second,
			},
		},
	}

	if templateStr != "" {
		tmpl, err := template.New("webhook").Parse(templateStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse template: %w", err)
		}
		w.Template = tmpl
	}

	return w, nil
}

// TestResult contains the result of a webhook test
type TestResult struct {
	Success    bool
	StatusCode int
	Error      string
}

// Send sends a notification to the webhook URL
func (w *WebhookDispatcher) Send(ctx context.Context, notification types.Notification) error {
	result := w.SendTest(ctx, notification)
	if !result.Success {

View on GitHub (pinned to d9463cbe21)