{"record":{"id":"3e95f40827faeaac","repo":"affaan-m/ECC","slug":"returned","errorCode":null,"errorMessage":"{} returned {}","messagePattern":"(.+?) returned (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/notifications.rs","lineNumber":419,"sourceCode":"    Ok(())\n}\n\n#[cfg(not(test))]\nfn send_webhook_request(target: &WebhookTarget, payload: serde_json::Value) -> Result<()> {\n    let agent = ureq::Agent::config_builder()\n        .timeout_connect(Some(std::time::Duration::from_secs(5)))\n        .timeout_recv_response(Some(std::time::Duration::from_secs(5)))\n        .build()\n        .new_agent();\n    let response = agent\n        .post(&target.url)\n        .send_json(payload)\n        .with_context(|| format!(\"POST {}\", target.url))?;\n\n    if response.status().is_success() {\n        Ok(())\n    } else {\n        anyhow::bail!(\"{} returned {}\", target.url, response.status());\n    }\n}\n\n#[cfg(test)]\nfn send_webhook_request(_target: &WebhookTarget, _payload: serde_json::Value) -> Result<()> {\n    Ok(())\n}\n\nfn sanitize_osascript(value: &str) -> String {\n    value\n        .replace('\\\\', \"\")\n        .replace('\"', \"\\u{201C}\")\n        .replace('\\n', \" \")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/notifications.rs#L401-L437","documentation":"Webhook notification delivery POSTs a JSON payload to the configured target URL; if the HTTP response status is not a success (2xx), the code raises this error including the URL and the response status code. It indicates the endpoint was reachable but rejected or failed the request.","triggerScenarios":"Webhook target returns 4xx (bad URL path, invalid auth token, malformed payload per receiver's schema) or 5xx (receiver internal error / 429 too many requests).","commonSituations":"Slack/Discord webhook URLs rotated or revoked (401/404); payload schema changed on the receiver side (400); rate limiting by the webhook provider (429); receiver service down (502/503).","solutions":["Check the exact status code in the message: 4xx means fix the URL/token/payload, 5xx means retry later.","Verify the webhook URL is current and valid by testing with `curl -X POST`.","Confirm the payload matches what the receiver expects (field names, types).","Add retry with backoff for transient 5xx/429 responses; update the target configuration if the endpoint was rotated."],"exampleFix":"// before\n{\"targets\":[{\"url\":\"https://hooks.example.com/old-token\"}]}\n// after\n{\"targets\":[{\"url\":\"https://hooks.example.com/services/T000/B000/XXXX\"}]}","handlingStrategy":"retry","validationCode":"// validate the target before storing it\nlet resp = reqwest::blocking::Client::new().post(&url).json(&test_payload).send()?;\nif !resp.status().is_success() {\n    bail!(\"webhook target invalid: {} -> {}\", url, resp.status());\n}","typeGuard":null,"tryCatchPattern":"match send_webhook(target, payload) {\n    Err(e) if e.to_string().contains(\"returned 5\") || e.to_string().contains(\"returned 429\") => {\n        // retry with backoff\n        schedule_retry(target, payload, attempts + 1);\n    }\n    Err(e) => log::error!(\"webhook permanently failed: {e}\"),\n    Ok(()) => {}\n}","preventionTips":["Re-test webhook URLs after provider token rotation.","Validate payload schema against the receiver's docs.","Add exponential backoff for 5xx/429 statuses.","Monitor notification delivery and alert on persistent non-2xx responses."],"tags":["http","webhook","notifications","api"],"backgroundTag":"http-error-response","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}