nikivdev/code · error

GitHub API returned status {}: {}

Error message

GitHub API returned status {}: {}

What it means

Raised by `fetch_latest_release` when the GitHub releases API responds with a non-2xx status. The error embeds the HTTP status code and the (truncated) response body, so it covers rate limiting (403), repo not found (404), auth issues, and 5xx outages.

Source

Thrown at src/upgrade.rs:204

        owner, repo
    );

    let mut request = client
        .get(&url)
        .header("User-Agent", format!("flow/{}", current_version()))
        .header("Accept", "application/vnd.github.v3+json")
        .timeout(Duration::from_secs(30));

    if let Some(token) = github_token() {
        request = request.bearer_auth(token);
    }

    let response = request
        .send()
        .context("Failed to fetch release info from GitHub")?;

    if !response.status().is_success() {
        bail!(
            "GitHub API returned status {}: {}",
            response.status(),
            response.text().unwrap_or_default()
        );
    }

    response
        .json::<GitHubRelease>()
        .context("Failed to parse GitHub release response")
}

/// Fetch a release by tag (e.g. "v0.1.0") from GitHub.
fn fetch_release_by_tag(client: &Client, tag: &str) -> Result<GitHubRelease> {
    let (owner, repo) = upgrade_repo()?;
    let url = format!(
        "https://api.github.com/repos/{}/{}/releases/tags/{}",
        owner, repo, tag
    );

View on GitHub (pinned to a747e741ae)

Solutions

  1. If status is 403 with rate-limit body, wait for the rate-limit window to reset or set a GITHUB_TOKEN that the client can use.
  2. Verify the configured owner/repo (FLOW_UPGRADE_REPO) exists and has releases.
  3. Check https://www.githubstatus.com if you see 5xx statuses and retry later.
  4. Check proxy/firewall interference with api.github.com.

Example fix

// before
let status = check_for_upgrade_prompt();
// after
match check_for_upgrade_prompt() {
    Ok(Some(msg)) => println!("{msg}"),
    Ok(None) => {}
    Err(e) if e.to_string().contains("403") => eprintln!("rate limited; skipping check"),
    Err(e) => eprintln!("upgrade check failed: {e}"),
}
Defensive patterns

Strategy: retry

Validate before calling

fn github_reachable() -> bool {
    reqwest::blocking::get("https://api.github.com/rate_limit")
        .map(|r| r.status().is_success())
        .unwrap_or(false)
}
// and before calls: check remaining rate limit via /rate_limit endpoint

Try / catch

for attempt in 0..3 {
    match fetch_latest_release(owner, repo) {
        Ok(rel) => { /* use rel */ break; }
        Err(e) if e.to_string().contains("status 5") && attempt < 2 => {
            std::thread::sleep(std::time::Duration::from_secs(2 << attempt)); // retry on 5xx
        }
        Err(e) if e.to_string().contains("status 403") => {
            eprintln!("GitHub rate limited; set GITHUB_TOKEN or wait");
            break;
        }
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: Calling `fetch_latest_release` (via `run` or `check_for_upgrade_prompt`) when the GET to https://api.github.com/repos/<owner>/<repo>/releases/latest fails: 403 rate limit (unauthenticated GitHub API limit ~60/hr), 404 wrong owner/repo, 401 bad token, 5xx GitHub outage.

Common situations: CI servers hammering the API from shared IPs and hitting rate limits; FLOW_UPGRADE_REPO pointing at a renamed or deleted repo; corporate proxies returning error pages; GitHub incidents.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/050ed898682af3cb. Report an issue: GitHub.