Kuberwastaken/claurst · error
no tag_name in GitHub API response
Error message
no tag_name in GitHub API response
What it means
fetch_latest_version parsed the GitHub releases/latest API response successfully, but the JSON had no string 'tag_name' field. The code uses get("tag_name").and_then(as_str).ok_or_else(anyhow!) to bail. GitHub guarantees tag_name on release objects, so this usually means the response was not the expected release payload (e.g. a rate-limit or error JSON body that still parsed).
Solutions
- Check GitHub API rate-limit headers (X-RateLimit-Remaining) and wait or authenticate with GITHUB_TOKEN.
- Print the raw response body to see what JSON was actually returned.
- Verify the releases URL points at api.github.com/repos/<owner>/<repo>/releases/latest.
- Check the GitHub API changelog for schema changes to the release object.
Example fix
// before
let tag = json.get("tag_name").and_then(|v| v.as_str()).ok_or_else(|| anyhow!("no tag_name in GitHub API response"))?;
// after
let tag = json.get("tag_name")
.and_then(|v| v.as_str())
.or_else(|| json.get("message").and_then(|m| m.as_str()))
.ok_or_else(|| anyhow!("no tag_name in GitHub API response: {}", json))?; Defensive patterns
Strategy: type-guard
Validate before calling
// validate response shape before treating it as a release
if json.get("tag_name").and_then(|v| v.as_str()).is_none() {
eprintln!("unexpected GitHub response: {}", json);
} Type guard
fn extract_tag(json: &serde_json::Value) -> Option<&str> {
json.get("tag_name").and_then(|v| v.as_str())
} Try / catch
// surface the full body when the shape is wrong
let tag = extract_tag(&json)
.ok_or_else(|| anyhow!("no tag_name in GitHub API response: {}", json))?; Prevention
- Check X-RateLimit-Remaining and use an authenticated GITHUB_TOKEN for API calls
- Log the raw response body when parsing fails
- Assert the URL is api.github.com/repos/<owner>/<repo>/releases/latest
When it happens
Trigger: GET to the GitHub releases endpoint returns 2xx but the JSON lacks tag_name: an error/rate-limit body, a redirect landing page, or a changed/empty response shape in run_upgrade's version check.
Common situations: GitHub API rate limiting returning a JSON error object with an unexpected status handling; API schema change; proxy returning alternate JSON; hitting a mirror of api.github.com.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Bridge poll: auth error
- No access_token in response
- No API key found
- Failed to parse token response
- installed binary path has no parent
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/37d66d1209534cfb.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/cli/src/upgrade.rs:196
// ---------------------------------------------------------------------------
// GitHub API: latest version
// ---------------------------------------------------------------------------
async fn fetch_latest_version() -> Result<String> {
let url = format!("https://api.github.com/repos/{}/releases/latest", REPO);
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.user_agent(format!("claurst-upgrade/{}", env!("CARGO_PKG_VERSION")))
.build()?;
let resp = client.get(&url).send().await?;
if !resp.status().is_success() {
bail!("GitHub API returned {} for {}", resp.status(), url);
}
let json: serde_json::Value = resp.json().await?;
let tag = json
.get("tag_name")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("no tag_name in GitHub API response"))?;
Ok(tag.trim_start_matches('v').to_string())
}
// ---------------------------------------------------------------------------
// Download
// ---------------------------------------------------------------------------
async fn download_to_file(url: &str, dest: &Path) -> Result<()> {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(120))
.user_agent(format!("claurst-upgrade/{}", env!("CARGO_PKG_VERSION")))
.build()?;
let resp = client.get(url).send().await?;
if !resp.status().is_success() {
bail!(
"Download failed: HTTP {} for {}\n\
Check that this version exists in the releases page.",
resp.status(),View on GitHub (pinned to b0637c97ec)