{"record":{"id":"0f0c83671f90c3cc","repo":"Hmbown/CodeWhale","slug":"failed-to-fetch-description-from-url-http-st","errorCode":null,"errorMessage":"failed to fetch {description} from {url}: HTTP {status}\n{body}","messagePattern":"failed to fetch (.+?) from (.+?): HTTP (.+?)\n(.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/update.rs","lineNumber":979,"sourceCode":"        .get(url)\n        .header(reqwest::header::ACCEPT, \"application/vnd.github+json\")\n        .send()\n        .with_context(|| format!(\"failed to fetch {description} from {url}\"))?;\n    let status = response.status();\n    let body = response\n        .text()\n        .with_context(|| format!(\"failed to read {description} response body from {url}\"))?;\n    Ok((status, body))\n}\n\nfn fetch_release_json(url: &str, description: &str, proxy: Option<&Proxy>) -> Result<String> {\n    let mut last_error = None;\n    for attempt in 1..=UPDATE_HTTP_ATTEMPTS {\n        match fetch_release_json_once(url, description, proxy) {\n            Ok((status, body)) if status.is_success() => return Ok(body),\n            Ok((status, body)) => {\n                let error =\n                    anyhow!(\"failed to fetch {description} from {url}: HTTP {status}\\n{body}\");\n                if should_retry_http_status(status) && attempt < UPDATE_HTTP_ATTEMPTS {\n                    last_error = Some(error);\n                    sleep_before_update_retry(attempt);\n                    continue;\n                }\n                return Err(error);\n            }\n            Err(error) if attempt < UPDATE_HTTP_ATTEMPTS => {\n                last_error = Some(error);\n                sleep_before_update_retry(attempt);\n            }\n            Err(error) => return Err(error),\n        }\n    }\n    Err(last_error.unwrap_or_else(|| anyhow!(\"failed to fetch {description} from {url}\")))\n}\n\nfn should_retry_http_status(status: reqwest::StatusCode) -> bool {","sourceCodeStart":961,"sourceCodeEnd":997,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/cli/src/update.rs#L961-L997","documentation":"fetch_release_json retrieves release metadata JSON and got an HTTP response with a non-success status; the status and body are embedded for diagnosis. Retryable statuses (5xx, 408, 429) are retried up to 3 times with linear backoff before this surfaces; other 4xx statuses fail immediately.","triggerScenarios":"The update-check path fetching a release JSON when the endpoint returns 403 (GitHub unauthenticated rate limit), 404 (wrong repo/tag or removed release), 401, a proxy interception status, or a 5xx that persisted across all 3 attempts.","commonSituations":"GitHub API rate limiting (60 req/h per IP without a token); corporate proxies or MITM appliances returning 4xx for github.com; an update-feed URL pointing at a wrong repo; a release deleted after metadata was cached.","solutions":["Read the HTTP status in the message: 403 with a rate-limit body means wait for the reset window or configure the proxy/token; 404 means verify the feed URL and that the release exists","If the body contains GitHub rate-limit JSON, honor the reset timestamp and retry after it","Check HTTP_PROXY/HTTPS_PROXY and Codewhale's proxy setting for a misconfigured intercept","For 5xx that persisted 3 attempts, retry later; the server side is transiently broken"],"exampleFix":"# before: unauthenticated API hammering from CI\nfor i in $(seq 1 100); do codewhale update check; done\n\n# after: check once, honor rate limits\n codewhale update check  # retry only after the rate-limit reset reported in the body","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Match on the error, extract the 'HTTP <status>' and body from the message, and branch: for 429/403 parse the rate-limit reset in the body and retry only after it; for 5xx retry with capped backoff; for other 4xx surface immediately with the body text.","preventionTips":["Configure Codewhale's proxy setting instead of hammering github.com unauthenticated from shared CI IPs","Cache release metadata between runs to stay under GitHub rate limits","Log the response body once per failure, not per retry"],"tags":["network","http","github-api","rate-limit","self-update"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}