{"record":{"id":"ac13231faf65975c","repo":"astrid-runtime/astrid","slug":"github-api-returned","errorCode":null,"errorMessage":"GitHub API returned {}","messagePattern":"GitHub API returned (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/capsule/install_update.rs","lineNumber":54,"sourceCode":"    repo: &str,\n) -> anyhow::Result<semver::Version> {\n    let api_url = format!(\"https://api.github.com/repos/{org}/{repo}/releases/latest\");\n    let response = client\n        .get(&api_url)\n        .send()\n        .await\n        .context(\"failed to reach GitHub API\")?;\n\n    if response.status() == reqwest::StatusCode::NOT_FOUND {\n        bail!(\"no GitHub releases found for {org}/{repo}\");\n    }\n    if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS\n        || response.status() == reqwest::StatusCode::FORBIDDEN\n    {\n        bail!(\"GitHub API rate limit exceeded - try again later\");\n    }\n    if !response.status().is_success() {\n        bail!(\"GitHub API returned {}\", response.status());\n    }\n\n    let json: serde_json::Value = response\n        .json()\n        .await\n        .context(\"failed to parse GitHub API response\")?;\n    let tag_name = json\n        .get(\"tag_name\")\n        .and_then(serde_json::Value::as_str)\n        .filter(|s| !s.is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"GitHub release has missing or empty tag_name\"))?;\n\n    let version_str = strip_version_prefix(tag_name);\n    semver::Version::parse(version_str)\n        .with_context(|| format!(\"GitHub tag '{tag_name}' is not valid semver\"))\n}\n\n/// Check whether a newer version is available from a capsule's source.","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/capsule/install_update.rs#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"GitHub partial outage or degraded API; corporate proxy returning 407/401; expired GITHUB_TOKEN causing 401; intermediary firewalls returning unexpected statuses.","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."],"exampleFix":"// before: stale token in CI\nexport GITHUB_TOKEN=ghp_expired\n// error: GitHub API returned 401 Unauthorized\n\n// after\nunset GITHUB_TOKEN  # or refresh to a valid token\nastrid capsule update --workspace","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match check_remote_version(&client, &source, &version) {\n    UpdateCheck::Failed { reason } if reason.starts_with(\"GitHub API returned 5\") => {\n        eprintln!(\"GitHub outage, retrying once...\");\n        retry_with_backoff(2, Duration::from_secs(10), || check()).await\n    },\n    other => handle(other),\n}","preventionTips":["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."],"tags":["network","github","http"],"backgroundTag":"http-error-response","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}