astrid-runtime/astrid · warning

GitHub API rate limit exceeded - try again later

Error message

GitHub API rate limit exceeded - try again later

What it means

Raised by `fetch_github_latest_version` when GitHub responds with 429 TOO_MANY_REQUESTS or 403 FORBIDDEN, which is how GitHub signals API rate limiting. Unauthenticated GitHub API calls are limited to 60 requests/hour per IP, and `astrid capsule update` can check many capsules in one run. The message explicitly tells you to retry later.

Source

Thrown at crates/astrid-cli/src/commands/capsule/install_update.rs:51

async fn fetch_github_latest_version(
    client: &reqwest::Client,
    org: &str,
    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"))

View on GitHub (pinned to affd8760f4)

Solutions

  1. Wait until the rate-limit window resets (check the `X-RateLimit-Reset` response header) and re-run `astrid capsule update`.
  2. Set GITHUB_TOKEN / provide authenticated GitHub credentials so the limit rises from 60 to 5,000 requests/hour, if your setup forwards them.
  3. Update only the specific capsules you need (`astrid capsule update --workspace <name>`) instead of all capsules.
  4. Check your network: switch off a shared VPN/runner IP or add caching in CI to avoid repeated update checks.

Example fix

// before: unauthenticated, shared-IP CI run fails
astrid capsule update --workspace
// error: GitHub API rate limit exceeded - try again later

// after: schedule less often and export a token
export GITHUB_TOKEN=ghp_...
astrid capsule update --workspace
Defensive patterns

Strategy: retry

Try / catch

match check_remote_version(&client, &source, &version) {
    UpdateCheck::Failed { reason } if reason.contains("rate limit exceeded") => {
        tokio::time::sleep(backoff).await;
        retry_with_backoff(3, backoff, || check_remote_version(&client, &source, &version)).await
    },
    other => handle(other),
}

Prevention

When it happens

Trigger: Running `astrid capsule update` in workspace mode when the machine's IP has exhausted the unauthenticated GitHub API quota (60 req/hr), e.g. CI runners sharing egress IPs, or checking many GitHub-sourced capsules in one batch. Also 403 from GitHub's abuse/rate-limit responses.

Common situations: CI pipelines on shared runners; corporate NAT with many users hitting api.github.com; bulk updates of dozens of capsules in a loop; running update repeatedly in a short window.

Related errors


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