knadh/listmonk · error
error unmarshalling SES notification: %v
Error message
error unmarshalling SES notification: %v
What it means
SES.ProcessBounce JSON-unmarshals the raw SNS POST body into sesNotif before signature verification. If the body is not valid JSON matching the SNS envelope shape, it returns 'error unmarshalling SES notification'. (The same message is reused if the inner n.Message JSON fails to unmarshal into sesMail.)
Source
Thrown at internal/bounce/webhooks/ses.go:116
if err != nil {
return fmt.Errorf("error requesting subscription URL: %v", err)
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("non 200 response on subscription URL: %v", resp.StatusCode)
}
return nil
}
// ProcessBounce processes an SES bounce notification and returns a Bounce object.
func (s *SES) ProcessBounce(b []byte) (models.Bounce, error) {
var (
bounce models.Bounce
n sesNotif
)
if err := json.Unmarshal(b, &n); err != nil {
return bounce, fmt.Errorf("error unmarshalling SES notification: %v", err)
}
if err := s.verifyNotif(n); err != nil {
return bounce, err
}
var m sesMail
if err := json.Unmarshal([]byte(n.Message), &m); err != nil {
return bounce, fmt.Errorf("error unmarshalling SES notification: %v", err)
}
if (m.EventType != "" && m.EventType != "Bounce") ||
(m.NotifType != "" && (m.NotifType != "Bounce" && m.NotifType != "Complaint")) {
return bounce, errors.New("notification type is not bounce")
}
if len(m.Mail.Destination) == 0 {
return bounce, errors.New("no destination e-mails found in SES notification")
}View on GitHub (pinned to 670c01717d)
Solutions
- Log the raw request body and validate it as an SNS envelope JSON (Type, MessageId, TopicArn, Message, Signature, SigningCertURL)
- Ensure SNS raw message delivery is DISABLED for the subscription so the full SNS envelope is delivered
- Verify the subscription points at the SES bounce route and that Message contains valid SES event JSON
- When testing manually, POST a complete signed SNS envelope rather than only the inner SES event
Example fix
// before (raw SES event posted directly)
{"eventType":"Bounce",...}
// after (SNS envelope)
{"Type":"Notification","MessageId":"id","TopicArn":"arn:aws:sns:...","Message":"{\"eventType\":\"Bounce\",...}","Timestamp":"...","SignatureVersion":"1","Signature":"...","SigningCertURL":"https://sns...pem"} Defensive patterns
Strategy: validation
Validate before calling
func validSESNotification(b []byte) bool {
var probe struct {
Type string `json:"Type"`
Message string `json:"Message"`
}
if json.Unmarshal(b, &probe) != nil { return false }
if probe.Type != "Notification" || probe.Message == "" { return false }
var mail struct {
EventType string `json:"eventType"`
NotifType string `json:"notificationType"`
}
return json.Unmarshal([]byte(probe.Message), &mail) == nil
}
// pre-validate envelope + inner Message before calling ProcessBounce Try / catch
bounce, err := handler.ProcessBounce(body)
if err != nil {
if strings.HasPrefix(err.Error(), "error unmarshalling SES notification") {
log.Printf("SES raw body: %s", string(body))
http.Error(w, "invalid SES payload", http.StatusBadRequest)
return
}
http.Error(w, "webhook error", http.StatusInternalServerError)
} Prevention
- Disable SNS raw message delivery so the full envelope wraps the SES event
- Ensure Message contains a valid SES event JSON (eventType/notificationType)
- Subscribe the topic to the correct SES bounce route
- Test with real signed SNS notifications, not bare SES event JSON
When it happens
Trigger: The SES webhook receives a body that fails json.Unmarshal into sesNotif: empty/HTML body, malformed JSON, non-SNS JSON payload, or wrong types for envelope fields; also when n.Message (the embedded SES event string) is not valid JSON for sesMail.
Common situations: Testing the endpoint with arbitrary JSON; a proxy returning an error page as body; pointing the SNS topic at the wrong route; SNS raw-message delivery enabled so the envelope shape differs; SES event format changes adding unexpected field types inside Message.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- error unmarshalling SNS notification: %v
- error unmarshalling postmark notification: %v
- error unmarshalling Sendgrid notification: %v
- error parsing lang file: %s: %v
- error reading validation data: %v
AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01).
Data as JSON: /api/errors/377709d5878d8965.
Report an issue: GitHub.