{"record":{"id":"cbbbdda936831d6a","repo":"knadh/listmonk","slug":"error-unmarshalling-azure-event-v","errorCode":null,"errorMessage":"error unmarshalling azure event: %v","messagePattern":"error unmarshalling azure event: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bounce/webhooks/azure.go","lineNumber":190,"sourceCode":"\nfunc secretsEqual(expected, given string) bool {\n\tif expected == \"\" || given == \"\" {\n\t\treturn false\n\t}\n\treturn subtle.ConstantTimeCompare([]byte(expected), []byte(given)) == 1\n}\n\nfunc parseAzureEvents(b []byte) ([]azureEvent, error) {\n\tvar raws []json.RawMessage\n\tif err := json.Unmarshal(b, &raws); err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshalling azure notification array: %v\", err)\n\t}\n\n\tevents := make([]azureEvent, 0, len(raws))\n\tfor _, raw := range raws {\n\t\tev := azureEvent{RawData: raw}\n\t\tif err := json.Unmarshal(raw, &ev); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error unmarshalling azure event: %v\", err)\n\t\t}\n\t\tevents = append(events, ev)\n\t}\n\n\treturn events, nil\n}\n\nfunc mapAzureStatus(status, details string) (string, bool) {\n\ts := strings.ToLower(strings.TrimSpace(status))\n\td := strings.ToLower(strings.TrimSpace(details))\n\n\tswitch s {\n\tcase \"bounced\", \"suppressed\":\n\t\treturn models.BounceTypeHard, true\n\tcase \"failed\":\n\t\t// Infer severity from SMTP-enhanced status if available.\n\t\tif m := reSMTPStatus.FindStringSubmatch(d); len(m) > 1 {\n\t\t\tif strings.HasPrefix(m[1], \"5.\") {","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/azure.go#L172-L208","documentation":"After the body parses as a JSON array, parseAzureEvents unmarshals each element into the azureEvent struct (eventType + data). This error is thrown when an individual array element is not a valid JSON object or contains fields whose types conflict with the struct (e.g. `data` is a string instead of an object).","triggerScenarios":"Calling ProcessSubscription or ProcessBounce with an array containing: raw strings/numbers instead of event objects, an event whose `eventType` is not a string, an event whose `data` is not a JSON object, or corrupt JSON in one element of a batch.","commonSituations":"Event Grid pushing a different subscription kind (e.g. storage/blob events with differently typed data) to the same endpoint; manual tests with placeholder arrays like `[\"test\"]`; a provider schema update changing field types; binary or truncated bodies from proxy misconfiguration.","solutions":["Confirm the events posted are Azure Communication Services email events where `data` is a JSON object with string fields.","Validate each array element is a JSON object before posting (or in tests, use a captured real Event Grid envelope).","Log the raw body and the wrapped json.Unmarshal error to identify which element/field conflicts.","If testing, use realistic payload samples rather than placeholder strings in the array."],"exampleFix":"// before: array of strings\nbody := []byte(`[\"event1\",\"event2\"]`)\n\n// after: array of event objects\nbody := []byte(`[{\"eventType\":\"Microsoft.Communication.EmailDeliveryReportReceived\",\"data\":{\"recipient\":\"a@b.com\",\"status\":\"Bounced\"}}]`)","handlingStrategy":"validation","validationCode":"func hasWellFormedEvents(body []byte) bool {\n    var evs []struct {\n        EventType string          `json:\"eventType\"`\n        Data      json.RawMessage `json:\"data\"`\n    }\n    if json.Unmarshal(body, &evs) != nil {\n        return false\n    }\n    for _, e := range evs {\n        if e.EventType == \"\" || !json.Valid(e.Data) {\n            return false\n        }\n        var obj map[string]any\n        if json.Unmarshal(e.Data, &obj) != nil {\n            return false\n        }\n    }\n    return len(evs) > 0\n}","typeGuard":"func isEventObject(raw json.RawMessage) bool {\n    var ev struct {\n        EventType string          `json:\"eventType\"`\n        Data      json.RawMessage `json:\"data\"`\n    }\n    if json.Unmarshal(raw, &ev) != nil || ev.EventType == \"\" {\n        return false\n    }\n    var obj map[string]any\n    return json.Unmarshal(ev.Data, &obj) == nil\n}","tryCatchPattern":"bounces, err := azure.ProcessBounce(req, body)\nif err != nil {\n    if strings.Contains(err.Error(), \"unmarshalling azure event\") {\n        log.Printf(\"bad event element in Event Grid batch: %v\", err)\n        http.Error(w, \"invalid event format\", http.StatusBadRequest)\n        return\n    }\n    http.Error(w, \"webhook processing failed\", http.StatusInternalServerError)\n}","preventionTips":["Pin your Event Grid subscription to expected Azure Communication Services event types and filter others out.","Test with real captured event envelopes rather than placeholder arrays.","Return 400 with the per-element error so the sender can diagnose bad elements."],"tags":["azure","webhook","json","event-grid","type-mismatch"],"backgroundTag":"webhook-payload-schema-mismatch","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}