Hmbown/CodeWhale · error · anyhow::Error

release redirect did not resolve to a tag URL: {final_url}

Error message

release redirect did not resolve to a tag URL: {final_url}

What it means

During a self-update, Codewhale resolves the 'latest' release by following GitHub's /releases/latest redirect and expects the final URL to contain /releases/tag/<tag> (or the tag in the HTML body). This error means the redirect succeeded (HTTP 2xx) but the final URL and the page body both lacked a parseable tag, so the version to install could not be determined.

Source

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

    url: &str,
) -> Result<String> {
    let response = client
        .get(url)
        .send()
        .with_context(|| format!("failed to fetch release redirect from {url}"))?;
    let status = response.status();
    let final_url = response.url().clone();
    if status.is_success() {
        if let Some(tag_name) = release_tag_from_github_release_url(&final_url) {
            return Ok(tag_name);
        }
        let body = response
            .text()
            .with_context(|| format!("failed to read release redirect response from {url}"))?;
        if let Some(tag_name) = release_tag_from_github_release_html(&body) {
            return Ok(tag_name);
        }
        bail!("release redirect did not resolve to a tag URL: {final_url}");
    }

    let body = response
        .text()
        .with_context(|| format!("failed to read release redirect response from {url}"))?;
    bail!("failed to fetch release redirect from {url}: HTTP {status}\n{body}");
}

fn release_tag_from_github_release_url(url: &reqwest::Url) -> Option<String> {
    let segments = url.path_segments()?.collect::<Vec<_>>();
    segments
        .windows(3)
        .find(|window| window[0] == "releases" && window[1] == "tag")
        .map(|window| window[2].to_string())
        .filter(|tag| !tag.is_empty())
}

fn release_tag_from_github_release_html(body: &str) -> Option<String> {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Retry the update shortly — transient GitHub page/interstitial serving usually resolves itself
  2. Bypass 'latest' resolution by updating to an explicit tag/version if the CLI supports pinning
  3. Inspect what the redirect actually returns: `curl -sIL https://github.com/<owner>/<repo>/releases/latest` and check the final Location/path
  4. Remove proxy/TLS-interception or captive-portal interference from the request path, or configure the CLI's proxy settings
  5. If the issue persists on every run, report it — the URL/HTML tag extraction in release_tag_from_github_release_url/_html needs updating for GitHub's current page shape

Example fix

# before
$ codewhale update   # resolves 'latest' via redirect

# after
$ curl -sIL https://github.com/codewhale/codewhale/releases/latest | grep -i location
# confirm it ends with /releases/tag/vX.Y.Z, then pin that version in the update
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight the redirect shape yourself before running the updater:
let resp = client.get(latest_url).send().await?;
let final_url = resp.url();
let is_tag = final_url.path_segments()
    .is_some_and(|mut s| s.any(|seg| seg == "tag"));
if resp.status().is_success() && !is_tag {
    // expect lookup failure; surface a clear message now
}

Try / catch

// Wrap latest-tag resolution; on 'did not resolve to a tag URL' retry with backoff,
// then fall back to a pinned version:
for attempt in 0..3 {
    match resolve_latest_tag(&client).await {
        Ok(tag) => return Ok(tag),
        Err(e) if e.to_string().contains("did not resolve to a tag URL") => {
            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;
        }
        Err(e) => return Err(e),
    }
}
bail!("fall back to explicit --version pin")

Prevention

When it happens

Trigger: Fetching https://github.com/<owner>/<repo>/releases/latest returned 200 with a final URL whose path has no 'releases/tag/<non-empty-tag>' 3-segment window and whose HTML does not contain a recognizable tag reference — e.g. GitHub A/B UI changes, an interstitial/login/region block page, or a proxy that rewrites redirect targets.

Common situations: GitHub serving a changed page layout to the HTML fallback parser; a corporate proxy or captive portal intercepting the redirect and returning its own 200 page; a mirror or GHE host with different URL structure; a repository whose default 'latest' page is unavailable (no published releases).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/c1b484dcdac02b35. Report an issue: GitHub.