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.

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.

Appendix: 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)