{"record":{"id":"4ebe9602e1253c94","repo":"knadh/listmonk","slug":"webhook-key-is-not-configured","errorCode":null,"errorMessage":"webhook key is not configured","messagePattern":"webhook key is not configured","errorType":"http","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/bounce/webhooks/forwardemail.go","lineNumber":51,"sourceCode":"\tResponseCode    int               `json:\"response_code\"`\n\tTruthSource     string            `json:\"truth_source\"`\n\tHeaders         map[string]string `json:\"headers\"`\n\tBounce          BounceDetails     `json:\"bounce\"`\n\tBouncedAt       time.Time         `json:\"bounced_at\"`\n}\n\n// Forwardemail handles webhook notifications (mainly bounce notifications).\ntype Forwardemail struct {\n\thmacKey []byte\n}\n\nfunc NewForwardemail(key []byte) *Forwardemail {\n\treturn &Forwardemail{hmacKey: key}\n}\n\nfunc (p *Forwardemail) ProcessBounce(sigHex string, body []byte) ([]models.Bounce, error) {\n\tif len(p.hmacKey) == 0 {\n\t\treturn nil, errors.New(\"webhook key is not configured\")\n\t}\n\n\t// Decode the hex-encoded signature from the webhook\n\tsig, err := hex.DecodeString(sigHex)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid signature encoding: %v\", err)\n\t}\n\n\t// Generate HMAC using the request body and secret key\n\tmac := hmac.New(sha256.New, p.hmacKey)\n\tmac.Write(body)\n\texpectedSignature := mac.Sum(nil)\n\n\t// Compare the generated signature with the provided signature\n\tif !hmac.Equal(expectedSignature, sig) {\n\t\treturn nil, errors.New(\"invalid signature\")\n\t}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/knadh/listmonk/blob/670c01717d48647093335cc23a6be6f4b79c3b6b/internal/bounce/webhooks/forwardemail.go#L33-L69","documentation":"Forwardemail.ProcessBounce verifies webhook payloads with HMAC-SHA256 using an hmacKey supplied at construction (NewForwardemail). This error is returned when that key is empty (len == 0), meaning no verification is possible. The library refuses to process rather than skipping signature verification, which would be a security hole.","triggerScenarios":"Forwardemail was constructed as NewForwardemail(nil) or NewForwardemail([]byte{}) — typically because the webhook key config value/env var was empty — and then ProcessBounce is called with an incoming signature and body.","commonSituations":"Missing FORWARD_EMAIL_WEBHOOK_KEY-style env var in the deployment; config key omitted or empty string in the config file; a fresh environment (staging, local dev) where secrets were never provisioned; Docker/secret-mount failed so the value reads as empty.","solutions":["Set the Forwardemail webhook key in your configuration/environment and restart so NewForwardemail receives a non-empty key.","Copy the webhook signing secret from your Forwardemail account/dashboard into the deployment secrets store.","Check the code path that reads the key and fails loudly at startup (validate len(key) > 0 when constructing NewForwardemail) instead of at first webhook.","Verify secrets mounts/env propagation (Docker secret file, k8s secret) actually populated the variable."],"exampleFix":"// before\nkey := os.Getenv(\"FORWARD_EMAIL_KEY\") // \"\" if unset\np := webhooks.NewForwardemail([]byte(key))\n// after\nkey := os.Getenv(\"FORWARD_EMAIL_KEY\")\nif key == \"\" {\n\tlog.Fatal(\"FORWARD_EMAIL_KEY must be set\")\n}\np := webhooks.NewForwardemail([]byte(key))","handlingStrategy":"validation","validationCode":"key := os.Getenv(\"FORWARD_EMAIL_WEBHOOK_KEY\")\nif key == \"\" {\n\treturn errors.New(\"forwardemail webhook key must be configured before processing bounces\")\n}","typeGuard":"func forwardemailConfigured(p *webhooks.Forwardemail) bool {\n\treturn p != nil // combine with non-empty key check at construction time\n}","tryCatchPattern":"bounces, err := p.ProcessBounce(sigHex, body)\nif err != nil {\n\tif strings.Contains(err.Error(), \"webhook key is not configured\") {\n\t\t// deployment misconfiguration: alert ops, return 503\n\t\treturn echo.NewHTTPError(http.StatusServiceUnavailable, \"webhook not configured\")\n\t}\n\treturn err\n}","preventionTips":["Validate the key is non-empty at startup, when calling NewForwardemail, and fail fast.","Provision the secret in every environment (staging, dev) via your secrets manager.","Check Docker/K8s secret mounts actually populate the env var.","Add a smoke test that constructs the webhook with the production config shape."],"tags":["forwardemail","webhook","hmac","config"],"backgroundTag":"missing-env-var","analyzedSha":"670c01717d48647093335cc23a6be6f4b79c3b6b","analyzedAt":"2026-09-01T03:39:35.452Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}