astrid-runtime/astrid · error
GitHub API returned {}
Error message
GitHub API returned {} What it means
Raised by `fetch_github_latest_version` for any GitHub API response that is not 404, 429/403, and not a 2xx success — e.g. 500/502/503 server errors, 401 unauthorized, or other unexpected statuses. It passes the raw HTTP status through so the developer can see exactly what GitHub returned.
Source
Thrown at crates/astrid-cli/src/commands/capsule/install_update.rs:54
repo: &str,
) -> anyhow::Result<semver::Version> {
let api_url = format!("https://api.github.com/repos/{org}/{repo}/releases/latest");
let response = client
.get(&api_url)
.send()
.await
.context("failed to reach GitHub API")?;
if response.status() == reqwest::StatusCode::NOT_FOUND {
bail!("no GitHub releases found for {org}/{repo}");
}
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.View on GitHub (pinned to affd8760f4)
Solutions
- Read the status in the message: for 5xx, check https://www.githubstatus.com and retry once GitHub recovers.
- For 401/Unexpected auth statuses, refresh or remove an invalid GITHUB_TOKEN.
- Check proxy/firewall configuration (HTTPS_PROXY) if the status comes from a middlebox rather than GitHub.
- Retry after a short backoff; transient 502/503 responses often resolve on their own.
Example fix
// before: stale token in CI export GITHUB_TOKEN=ghp_expired // error: GitHub API returned 401 Unauthorized // after unset GITHUB_TOKEN # or refresh to a valid token astrid capsule update --workspace
Defensive patterns
Strategy: retry
Try / catch
match check_remote_version(&client, &source, &version) {
UpdateCheck::Failed { reason } if reason.starts_with("GitHub API returned 5") => {
eprintln!("GitHub outage, retrying once...");
retry_with_backoff(2, Duration::from_secs(10), || check()).await
},
other => handle(other),
} Prevention
- Check https://www.githubstatus.com before bulk update runs.
- Use short exponential backoff for transient 5xx statuses.
- Keep GITHUB_TOKEN valid and rotated to avoid 401s surfacing here.
- Audit HTTPS_PROXY settings so middleboxes do not inject unexpected statuses.
When it happens
Trigger: Calling `astrid capsule update` (workspace mode) when api.github.com returns a non-success, non-404, non-rate-limit status for `/repos/{org}/{repo}/releases/latest` — GitHub incidents/outages (5xx), auth failures (401) when credentials are sent but invalid, or proxies intercepting the request.
Common situations: GitHub partial outage or degraded API; corporate proxy returning 407/401; expired GITHUB_TOKEN causing 401; intermediary firewalls returning unexpected statuses.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- no GitHub releases found for {org}/{repo}
- GitHub API rate limit exceeded - try again later
- GitHub API returned {} fetching release {resolved_ref} of {o
- GitHub API error querying release tag {candidate} for {org}/
- GitHub API returned {} for {org}/{repo} latest release
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ac13231faf65975c.
Report an issue: GitHub.