{"record":{"id":"315c5b8db12459e0","repo":"knadh/listmonk","slug":"missing-validationcode-in-subscription-payload","errorCode":null,"errorMessage":"missing validationCode in subscription payload","messagePattern":"missing validationCode in subscription payload","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bounce/webhooks/azure.go","lineNumber":82,"sourceCode":"\t// Validation code arrives in the first event for subscription validation flow.\n\tvar payload map[string]json.RawMessage\n\tif err := json.Unmarshal(events[0].RawData, &payload); err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading validation payload: %v\", err)\n\t}\n\n\trawData, ok := payload[\"data\"]\n\tif !ok {\n\t\treturn nil, errors.New(\"missing event data\")\n\t}\n\n\tvar data map[string]string\n\tif err := json.Unmarshal(rawData, &data); err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading validation data: %v\", err)\n\t}\n\n\tcode := strings.TrimSpace(data[\"validationCode\"])\n\tif code == \"\" {\n\t\treturn nil, errors.New(\"missing validationCode in subscription payload\")\n\t}\n\n\tres, _ := json.Marshal(map[string]string{\n\t\t\"validationResponse\": code,\n\t})\n\treturn json.RawMessage(res), nil\n}\n\n// ProcessBounce parses Azure Event Grid email delivery events and returns bounce entries.\nfunc (a *Azure) ProcessBounce(req *http.Request, b []byte) ([]models.Bounce, error) {\n\tif err := a.verifyAuth(req); err != nil {\n\t\treturn nil, err\n\t}\n\n\tevents, err := parseAzureEvents(b)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/azure.go#L64-L100","documentation":"After extracting the event data map, ProcessSubscription reads data[\"validationCode\"] and returns this error when it is empty or missing. Azure's subscription validation handshake requires echoing back this code as validationResponse; without it the subscription cannot be validated.","triggerScenarios":"A subscription validation event arrives whose data map has an empty or absent validationCode field — e.g. a manually crafted test payload, an event from a non-validation source routed to this handler, or whitespace-only validationCode (it is TrimSpace'd before the check).","commonSituations":"Testing the webhook with fake validation events lacking validationCode; Azure retries where the event was truncated; forwarding validation events through a queue/proxy that strips fields.","solutions":["Register the Event Grid subscription so a genuine SubscriptionValidationEvent containing validationCode is delivered (use the Azure portal/CLI handshake, not a fabricated payload).","Check the delivered event JSON and confirm data.validationCode is present and non-empty.","If testing, include a non-empty validationCode in the data map.","Confirm no intermediary transformation is dropping the validationCode field."],"exampleFix":"// before\ncurl -X POST https://host/webhooks/azure -d '{\"data\":{}}'\n// after\ncurl -X POST https://host/webhooks/azure -d '{\"data\":{\"validationCode\":\"abc123\"}}'","handlingStrategy":"validation","validationCode":"var probe struct {\n\tData struct {\n\t\tValidationCode string `json:\"validationCode\"`\n\t} `json:\"data\"`\n}\nif json.Unmarshal(rawData, &probe) != nil || strings.TrimSpace(probe.Data.ValidationCode) == \"\" {\n\t// invalid validation payload; do not call ProcessSubscription\n}","typeGuard":"func hasValidationCode(raw json.RawMessage) bool {\n\tvar d struct {\n\t\tValidationCode string `json:\"validationCode\"`\n\t}\n\tif json.Unmarshal(raw, &d) != nil { return false }\n\treturn strings.TrimSpace(d.ValidationCode) != \"\"\n}","tryCatchPattern":"res, err := azure.ProcessSubscription(body)\nif err != nil {\n\tif strings.Contains(err.Error(), \"missing validationCode\") {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"invalid validation event\")\n\t}\n\treturn err\n}","preventionTips":["Register the subscription via the Azure portal/CLI so a genuine handshake event is delivered.","Include a non-empty validationCode when testing manually.","Ensure proxies/queues preserve all fields of the event data.","Check Azure retries did not deliver truncated events."],"tags":["azure","event-grid","webhook","validation-code"],"backgroundTag":"missing-validation-code","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}