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

  1. Read the status in the message: for 5xx, check https://www.githubstatus.com and retry once GitHub recovers.
  2. For 401/Unexpected auth statuses, refresh or remove an invalid GITHUB_TOKEN.
  3. Check proxy/firewall configuration (HTTPS_PROXY) if the status comes from a middlebox rather than GitHub.
  4. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/ac13231faf65975c. Report an issue: GitHub.