aaif-goose/goose · error
GitHub API request failed: {}
Error message
GitHub API request failed: {} What it means
The `gh api repos/<repo>/contents` call executed but exited non-zero; goose surfaces gh's stderr verbatim after this prefix (crates/goose-cli/src/recipes/github_recipe.rs:265-268). The stderr text is the authoritative cause — typically HTTP 404 (no such repo), 403 (rate limit or missing scope), or 401 (bad token).
Source
Thrown at crates/goose-cli/src/recipes/github_recipe.rs:267
}
fn discover_github_recipes(repo: &str) -> Result<Vec<RecipeInfo>> {
use serde_json::Value;
use std::process::Command;
// Ensure GitHub CLI is authenticated
ensure_gh_authenticated()?;
// Get repository contents using GitHub CLI
let output = Command::new("gh")
.args(["api", &format!("repos/{}/contents", repo)])
.set_no_window()
.output()
.map_err(|e| anyhow!("Failed to fetch repository contents using 'gh api' command (executed when GOOSE_RECIPE_GITHUB_REPO is configured). This requires GitHub CLI (gh) to be installed and authenticated. Error: {}", e))?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(anyhow!("GitHub API request failed: {}", error_msg));
}
let contents: Value = serde_json::from_slice(&output.stdout)
.map_err(|e| anyhow!("Failed to parse GitHub API response: {}", e))?;
let mut recipes = Vec::new();
if let Some(items) = contents.as_array() {
for item in items {
if let (Some(name), Some(item_type)) = (
item.get("name").and_then(|n| n.as_str()),
item.get("type").and_then(|t| t.as_str()),
) {
if item_type == "dir" {
// Check if this directory contains a recipe file
if let Ok(recipe_info) = check_github_directory_for_recipe(repo, name) {
recipes.push(recipe_info);
}View on GitHub (pinned to 3810898a74)
Solutions
- Run the same call manually to see the exact status: `gh api repos/<owner>/<repo>/contents` — 404 means wrong name/no access, 403 usually rate limit
- Fix GOOSE_RECIPE_GITHUB_REPO to the correct owner/repo and confirm access with `gh repo view`
- If rate-limited, wait for the reset window (check `gh api rate_limit`) or authenticate with a token that raises the limit
Defensive patterns
Strategy: try-catch
Validate before calling
# Check the endpoint and rate limit headroom first gh api "repos/$GOOSE_RECIPE_GITHUB_REPO/contents" --jq 'length' gh api rate_limit --jq '.resources.core'
Try / catch
// Rust: always surface gh's stderr body with the exit status
if !output.status.success() {
anyhow::bail!(
"GitHub API request failed ({}): {}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
} Prevention
- Verify owner/repo spelling with `gh repo view` before configuring goose
- Authenticate with a token that has access to private/SSO recipe repos
- Back off when scripting discovery to avoid secondary rate limits
When it happens
Trigger: GOOSE_RECIPE_GITHUB_REPO names a nonexistent repo; the authenticated token lacks access to a private repo; GitHub API rate limit exceeded; network/proxy returning an error gh reports as failure.
Common situations: Typo in the env var value, private org repo where the token needs SSO authorization, heavy scripting hitting the secondary rate limit, or corporate proxies intercepting api.github.com.
Related errors
- Failed to access directory: {}
- GitHub API returned ${response.status}: ${response.statusTex
- Failed to parse GitHub API response: {}
- Update Available but no download URL found for platform: ${p
- Could not retrieve sub-recipe '{}': {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/d889e902d8aca73b.
Report an issue: GitHub.