{"record":{"id":"3cd82cab8aeedcc1","repo":"knadh/listmonk","slug":"error-unmarshalling-postmark-notification-v","errorCode":null,"errorMessage":"error unmarshalling postmark notification: %v","messagePattern":"error unmarshalling postmark notification: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bounce/webhooks/postmark.go","lineNumber":61,"sourceCode":"\nfunc NewPostmark(username, password string) *Postmark {\n\treturn &Postmark{\n\t\tauthHandler: middleware.BasicAuth(makePostmarkAuthHandler(username, password))(func(c echo.Context) error {\n\t\t\treturn nil\n\t\t}),\n\t}\n}\n\n// ProcessBounce processes Postmark bounce notifications and returns one object.\nfunc (p *Postmark) ProcessBounce(b []byte, c echo.Context) ([]models.Bounce, error) {\n\t// Do basicauth.\n\tif err := p.authHandler(c); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar n postmarkNotif\n\tif err := json.Unmarshal(b, &n); err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshalling postmark notification: %v\", err)\n\t}\n\n\t// Ignore irrelevant messages.\n\tif n.RecordType != \"Bounce\" && n.RecordType != \"SpamComplaint\" {\n\t\treturn nil, nil\n\t}\n\n\tsupportedBounceType := true\n\ttyp := models.BounceTypeHard\n\tswitch n.Type {\n\tcase \"HardBounce\", \"BadEmailAddress\", \"ManuallyDeactivated\":\n\t\ttyp = models.BounceTypeHard\n\tcase \"SoftBounce\", \"Transient\", \"DnsError\", \"SpamNotification\", \"VirusNotification\", \"DMARCPolicy\":\n\t\ttyp = models.BounceTypeSoft\n\tcase \"SpamComplaint\":\n\t\ttyp = models.BounceTypeComplaint\n\tdefault:\n\t\tsupportedBounceType = false","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/postmark.go#L43-L79","documentation":"Postmark's ProcessBounce first verifies basic-auth, then JSON-unmarshals the raw request body into postmarkNotif. If the body is not valid JSON or its fields don't match expected types (e.g. a string where an int is expected), the webhook returns 'error unmarshalling postmark notification'.","triggerScenarios":"The Postmark webhook endpoint receives a body that fails json.Unmarshal: empty body, HTML error page from a misrouted request, form-encoded data, malformed JSON, or a JSON body with wrong types for fields like ID (int) or Metadata (map[string]string).","commonSituations":"Testing the endpoint with cURL and sending plain text or missing Content-Type; a proxy returning an HTML 502 page as the body; Postmark schema changes adding unexpected types; pointing Postmark at the wrong URL so another route's response is posted; sending test payloads with Metadata values that aren't strings.","solutions":["Log the raw request body when this error occurs and validate it with a JSON linter","Verify Postmark webhook is configured to POST application/json to the correct listmonk bounce URL","Send a representative Postmark Bounce payload (with RecordType, ID as number, Metadata as string map) when testing","Check for proxies/load balancers returning HTML error bodies to the endpoint"],"exampleFix":"// before\ncurl -X POST .../bounce -d 'RecordType=Bounce'\n// after\ncurl -X POST .../bounce -H 'Content-Type: application/json' -d '{\"RecordType\":\"Bounce\",\"ID\":1,\"Type\":\"HardBounce\",\"Email\":\"a@b.c\",\"BouncedAt\":\"2024-01-01T00:00:00Z\",\"Metadata\":{}}'","handlingStrategy":"validation","validationCode":"func validPostmarkBody(b []byte) bool {\n    var probe struct {\n        RecordType string          `json:\"RecordType\"`\n        ID         int             `json:\"ID\"`\n        Type       string          `json:\"Type\"`\n        Email      string          `json:\"Email\"`\n        Metadata   map[string]string `json:\"Metadata\"`\n    }\n    return json.Unmarshal(b, &probe) == nil && probe.RecordType != \"\"\n}\n// return 400 with a clear message if !validPostmarkBody(body)","typeGuard":null,"tryCatchPattern":"bounces, err := handler.ProcessBounce(body, c)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"error unmarshalling postmark notification\") {\n        c.Logger().Errorf(\"postmark body: %s\", string(body))\n        return echo.NewHTTPError(http.StatusBadRequest, \"invalid JSON payload\")\n    }\n    return err\n}","preventionTips":["Always send application/json to the bounce endpoint","Use Postmark's official sample bounce payloads when testing","Capture and log raw bodies on 4xx to spot proxy-injected HTML pages","Pin Postmark webhook to Bounce and SpamComplaint events only"],"tags":["webhook","json","unmarshal","postmark","go"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}