astrid-runtime/astrid · error
GitHub release has missing or empty tag_name
Error message
GitHub release has missing or empty tag_name
What it means
`fetch_github_latest_version` reads the latest release from the GitHub API and extracts `tag_name` to derive the newest version. It throws when the JSON response has no `tag_name`, a non-string `tag_name`, or an empty one — i.e. the response shape is not a normal published release object.
Solutions
- Publish the release as non-draft with a valid tag_name in the GitHub UI (`gh release create v1.2.3`)
- Check the API response directly: `curl https://api.github.com/repos/{org}/{repo}/releases/latest` to see the actual payload
- If rate-limited or auth-walled, configure a GitHub token; if the repo has no releases, create one
Example fix
// before (release left as draft, tag_name absent) gh release create v1.2.3 --draft // after gh release create v1.2.3 // published release exposes tag_name
Defensive patterns
Strategy: retry
Validate before calling
// Validate the release payload before consuming:
// let tag = json.get("tag_name").and_then(|v| v.as_str()).filter(|s| !s.is_empty());
// if tag.is_none() { /* treat as no-release / retry or inspect raw response */ } Type guard
fn valid_tag_name(json: &serde_json::Value) -> Option<&str> {
json.get("tag_name")?.as_str().filter(|s| !s.is_empty())
} Try / catch
match result {
Err(e) if e.to_string().contains("missing or empty tag_name") => {
eprintln!("No published release found (draft? rate-limited? empty repo). Check the GitHub releases page.");
}
other => other?,
} Prevention
- Publish releases (not drafts) with valid non-empty tag_name
- Add a GitHub token in CI to avoid rate-limit JSON bodies being misread
- Verify the repo actually has releases before automated version checks
When it happens
Trigger: Querying a repo whose latest release is a draft (drafts omit tag_name in the public API); an empty repository with no releases returning an unexpected object; an API/proxy error body parsed as JSON; the repo's 'latest' endpoint returning a redirect/placeholder payload.
Common situations: Repositories that only publish draft releases; monorepos tagging without creating GitHub Releases; corporate proxies or rate-limit responses with JSON error bodies lacking tag_name; freshly created repos checked too early.
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
- GitHub API error querying release tag
- GitHub API returned fetching release of /
- GitHub API returned for / latest release
- GitHub releases URL cannot be a base
- grant preflight returned an unexpected response
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/5df6113950b5bf7c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/capsule/install_update.rs:65
}
if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS
|| response.status() == reqwest::StatusCode::FORBIDDEN
{
bail!("GitHub API rate limit exceeded - try again later");
}
if !response.status().is_success() {
bail!("GitHub API returned {}", response.status());
}
let json: serde_json::Value = response
.json()
.await
.context("failed to parse GitHub API response")?;
let tag_name = json
.get("tag_name")
.and_then(serde_json::Value::as_str)
.filter(|s| !s.is_empty())
.ok_or_else(|| anyhow::anyhow!("GitHub release has missing or empty tag_name"))?;
let version_str = strip_version_prefix(tag_name);
semver::Version::parse(version_str)
.with_context(|| format!("GitHub tag '{tag_name}' is not valid semver"))
}
/// Check whether a newer version is available from a capsule's source.
pub(super) async fn check_remote_version(
client: &reqwest::Client,
source: &str,
current_version: &str,
) -> UpdateCheck {
let Ok(current) = semver::Version::parse(current_version) else {
return UpdateCheck::Failed {
reason: format!("installed version '{current_version}' is not valid semver"),
};
};
View on GitHub (pinned to affd8760f4)