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
- Retry the update shortly — transient GitHub page/interstitial serving usually resolves itself
- Bypass 'latest' resolution by updating to an explicit tag/version if the CLI supports pinning
- Inspect what the redirect actually returns: `curl -sIL https://github.com/<owner>/<repo>/releases/latest` and check the final Location/path
- Remove proxy/TLS-interception or captive-portal interference from the request path, or configure the CLI's proxy settings
- 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
- Pin explicit versions in automation instead of relying on 'latest' redirect parsing
- Keep GitHub credentials/proxy configuration consistent for release fetches
- Alert on redirect final URLs that are not /releases/tag/* to catch GitHub page changes early
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
- failed to fetch release redirect from {url}: HTTP {status} {
- no release source published a usable {CHECKSUM_MANIFEST_ASSE
- GitHub release request failed with HTTP {status}: {body}
- failed to fetch {description} from {url}: HTTP {status} {bod
- failed to fetch {description} from {url}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/c1b484dcdac02b35.
Report an issue: GitHub.