Hmbown/CodeWhale · error · anyhow::Error

failed to fetch {description} from {url}: HTTP {status} {bod

Error message

failed to fetch {description} from {url}: HTTP {status}
{body}

What it means

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.

Source

Thrown at crates/cli/src/update.rs:979

        .get(url)
        .header(reqwest::header::ACCEPT, "application/vnd.github+json")
        .send()
        .with_context(|| format!("failed to fetch {description} from {url}"))?;
    let status = response.status();
    let body = response
        .text()
        .with_context(|| format!("failed to read {description} response body from {url}"))?;
    Ok((status, body))
}

fn fetch_release_json(url: &str, description: &str, proxy: Option<&Proxy>) -> Result<String> {
    let mut last_error = None;
    for attempt in 1..=UPDATE_HTTP_ATTEMPTS {
        match fetch_release_json_once(url, description, proxy) {
            Ok((status, body)) if status.is_success() => return Ok(body),
            Ok((status, body)) => {
                let error =
                    anyhow!("failed to fetch {description} from {url}: HTTP {status}\n{body}");
                if should_retry_http_status(status) && attempt < UPDATE_HTTP_ATTEMPTS {
                    last_error = Some(error);
                    sleep_before_update_retry(attempt);
                    continue;
                }
                return Err(error);
            }
            Err(error) if attempt < UPDATE_HTTP_ATTEMPTS => {
                last_error = Some(error);
                sleep_before_update_retry(attempt);
            }
            Err(error) => return Err(error),
        }
    }
    Err(last_error.unwrap_or_else(|| anyhow!("failed to fetch {description} from {url}")))
}

fn should_retry_http_status(status: reqwest::StatusCode) -> bool {

View on GitHub (pinned to 8880682c63)

Solutions

  1. 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
  2. If the body contains GitHub rate-limit JSON, honor the reset timestamp and retry after it
  3. Check HTTP_PROXY/HTTPS_PROXY and Codewhale's proxy setting for a misconfigured intercept
  4. For 5xx that persisted 3 attempts, retry later; the server side is transiently broken

Example fix

# before: unauthenticated API hammering from CI
for i in $(seq 1 100); do codewhale update check; done

# after: check once, honor rate limits
 codewhale update check  # retry only after the rate-limit reset reported in the body
Defensive patterns

Strategy: retry

Try / catch

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.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/0f0c83671f90c3cc. Report an issue: GitHub.