{"record":{"id":"3e95f40827faeaac","repo":"affaan-m/ECC","slug":"returned","errorCode":null,"errorMessage":"{} returned {}","messagePattern":"(.+?) returned (.+?)","errorType":"exception","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/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/notifications.rs#L401-L437","documentation":"Thrown by send_webhook_request (non-test build) when the configured webhook target URL returns an HTTP status that is not a 2xx success. The POST completed but the endpoint rejected or errored, so the webhook delivery is treated as failed.","triggerScenarios":"A webhook target (Slack, Discord, Teams, or custom) returns 4xx (bad URL, bad auth, malformed payload) or 5xx (endpoint down, rate limited with 429). Examples: a Slack incoming webhook URL that was revoked (403/404), a Discord webhook rate limit (429), or a custom endpoint that expects a different payload schema (400).","commonSituations":"Expired/rotated webhook URL; wrong payload format for the provider; destination service outage; rate limiting; network proxy returning 502/504; URL typo.","solutions":["Read the status code in the message: 401/403 means auth/URL is bad (regenerate the webhook URL); 400 means payload mismatch; 429 means rate limit (slow down or batch); 5xx means the endpoint is unhealthy (retry).","Re-copy the webhook URL from the provider (Slack/Discord/Teams app config) and update the target.","Confirm the payload schema matches the provider (the builder constructs provider-specific JSON; mixing providers and URLs fails).","Add retry with backoff for 429/5xx; do not retry 4xx except 428.","If behind a corporate proxy, ensure the proxy is configured and not returning an error page."],"exampleFix":"# before: revoked Slack webhook URL\necc2 notify set-webhook --url https://hooks.slack.com/services/T000/B000/OLD_TOKEN\n\n# after: regenerate the webhook in Slack and use the new URL\necc2 notify set-webhook --url https://hooks.slack.com/services/T000/B000/NEW_TOKEN","handlingStrategy":"retry","validationCode":"fn webhook_url_looks_valid(url: &str) -> bool {\n    url.starts_with(\"https://\") || url.starts_with(\"http://\")\n}\n\n// pre-flight: only POST if the URL is well-formed and the provider matches the payload builder","typeGuard":null,"tryCatchPattern":"match send_webhook_request(target, payload) {\n    Ok(()) => (),\n    Err(e) => {\n        let msg = format!(\"{e:#}\");\n        if msg.contains(\" 429 \") || msg.contains(\" returned 5\") {\n            // transient: schedule a retry with backoff\n            schedule_retry(target, payload);\n        } else {\n            // 4xx (except 429): do not retry; surface for human fix\n            log::error!(\"webhook permanently failed: {msg}\");\n        }\n    }\n}","preventionTips":["Regenerate and rotate webhook URLs on a schedule; do not hardcode long-lived tokens.","Match the payload schema to the provider whose URL you configured.","Retry 429/5xx with exponential backoff; never retry other 4xx.","Add a health check that does a lightweight test POST when the URL changes."],"tags":["notifications","webhook","network","http"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}