knadh/listmonk · error
error unmarshalling Lettermint notification: %v
Error message
error unmarshalling Lettermint notification: %v
What it means
After signature verification succeeds, Lettermint's ProcessBounce unmarshals the body into the lettermintNotif struct. This error is thrown when the signed body is not valid JSON or its field types conflict with the struct (e.g. `data` not an object, `event` not a string, nested `response` of wrong shape).
Source
Thrown at internal/bounce/webhooks/lettermint.go:77
}
// Decode the hex signature from the header.
sigB, err := hex.DecodeString(strings.TrimSpace(sigHex))
if err != nil {
return nil, fmt.Errorf("invalid signature encoding: %v", err)
}
// Compute HMAC-SHA256 of "{timestamp}.{body}" and compare.
mac := hmac.New(sha256.New, l.hmacKey)
mac.Write([]byte(fmt.Sprintf("%d.%s", ts, body)))
if !hmac.Equal(mac.Sum(nil), sigB) {
return nil, fmt.Errorf("invalid signature")
}
var n lettermintNotif
if err := json.Unmarshal(body, &n); err != nil {
return nil, fmt.Errorf("error unmarshalling Lettermint notification: %v", err)
}
// Map event to bounce type.
var typ string
switch n.Event {
case "message.hard_bounced":
typ = models.BounceTypeHard
case "message.soft_bounced":
typ = models.BounceTypeSoft
case "message.spam_complaint":
typ = models.BounceTypeComplaint
default:
// Ignore irrelevant events (e.g. webhook.test).
return nil, nil
}
campUUID := ""
if len(n.Data.Metadata) > 0 {View on GitHub (pinned to 670c01717d)
Solutions
- Confirm the body is valid JSON matching Lettermint's bounce event schema: {id, event, timestamp, data:{message_id, recipient, ...}}.
- Log the wrapped json.Unmarshal error — Go's error names the exact field/type conflict.
- Use a real captured Lettermint webhook payload (signed) as a test fixture instead of a hand-built one.
- Check you wired the correct Lettermint webhook (bounce events) to this endpoint.
Example fix
// before: data as a string
body := []byte(`{"event":"message.hard_bounced","data":"user@example.com"}`)
// after: data as an object
body := []byte(`{"event":"message.hard_bounced","data":{"message_id":"m1","recipient":"user@example.com"}}`) Defensive patterns
Strategy: validation
Validate before calling
func isLettermintBouncePayload(body []byte) bool {
if !json.Valid(body) {
return false
}
var probe struct {
Event string `json:"event"`
Data struct {
Recipient string `json:"recipient"`
} `json:"data"`
}
return json.Unmarshal(body, &probe) == nil && probe.Event != "" && probe.Data.Recipient != ""
} Try / catch
bounces, err := lm.ProcessBounce(sig, body)
if err != nil {
if strings.Contains(err.Error(), "unmarshalling Lettermint notification") {
log.Printf("lettermint payload schema mismatch: %v; body=%s", err, body)
http.Error(w, "invalid payload", http.StatusBadRequest)
return
}
http.Error(w, "processing failed", http.StatusInternalServerError)
} Prevention
- Use captured real Lettermint bounce payloads as test fixtures, not hand-built JSON.
- Validate json.Valid(body) and the presence of event/data.recipient before processing.
- Track Lettermint webhook schema updates; pin the handler tests to the documented schema.
- Note that unknown events (e.g. webhook.test) return nil,nil by design — only schema violations throw this error.
When it happens
Trigger: Calling ProcessBounce with a validly-signed but non-JSON or wrong-shaped body: empty body, plain text, `data` as a string instead of an object, `metadata` conflicting with RawMessage expectations in unexpected ways, or a payload from a different webhook (e.g. Lettermint's other event endpoints).
Common situations: Testing with a signature computed over a dummy body that isn't the actual JSON; Lettermint updating their event schema; posting the wrong Lettermint webhook type to the bounce endpoint; hand-written test fixtures with typo'd field types (numbers where strings are expected).
Related errors
- error unmarshalling Forwardemail notification: %v
- error reading validation data: %v
- error unmarshalling azure notification array: %v
- error unmarshalling azure event: %v
- webhook key is not configured
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/de0fc86c90335f23.
Report an issue: GitHub.