affaan-m/ECC · error · anyhow::Error
returned
Error message
{} returned {} What it means
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.
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.
Example fix
// before
{"targets":[{"url":"https://hooks.example.com/old-token"}]}
// after
{"targets":[{"url":"https://hooks.example.com/services/T000/B000/XXXX"}]} Defensive patterns
Strategy: retry
Validate before calling
// validate the target before storing it
let resp = reqwest::blocking::Client::new().post(&url).json(&test_payload).send()?;
if !resp.status().is_success() {
bail!("webhook target invalid: {} -> {}", url, resp.status());
} Try / catch
match send_webhook(target, payload) {
Err(e) if e.to_string().contains("returned 5") || e.to_string().contains("returned 429") => {
// retry with backoff
schedule_retry(target, payload, attempts + 1);
}
Err(e) => log::error!("webhook permanently failed: {e}"),
Ok(()) => {}
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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).
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Discord webhook request failed
- (payload.error || status + ' ' + statusText)
- unsupported plan-canvas request path
- announcement delivered; pending receipt cleanup requires…
- Codex review failed ` : ''}
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/3e95f40827faeaac.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/notifications.rs:419
Ok(())
}
#[cfg(not(test))]
fn send_webhook_request(target: &WebhookTarget, payload: serde_json::Value) -> Result<()> {
let agent = ureq::Agent::config_builder()
.timeout_connect(Some(std::time::Duration::from_secs(5)))
.timeout_recv_response(Some(std::time::Duration::from_secs(5)))
.build()
.new_agent();
let response = agent
.post(&target.url)
.send_json(payload)
.with_context(|| format!("POST {}", target.url))?;
if response.status().is_success() {
Ok(())
} else {
anyhow::bail!("{} returned {}", target.url, response.status());
}
}
#[cfg(test)]
fn send_webhook_request(_target: &WebhookTarget, _payload: serde_json::Value) -> Result<()> {
Ok(())
}
fn sanitize_osascript(value: &str) -> String {
value
.replace('\\', "")
.replace('"', "\u{201C}")
.replace('\n', " ")
}
#[cfg(test)]
mod tests {
use super::{View on GitHub (pinned to 8321021c54)