aaif-goose/goose · error
Failed to fetch repository contents using 'gh api' command (
Error message
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: {} What it means
discover_github_recipes lists available recipes by running `gh api repos/<repo>/contents` (crates/goose-cli/src/recipes/github_recipe.rs:250-263). This error is the spawn io failure of that command, mapped to a message explaining it requires the GitHub CLI installed and authenticated. It fires after ensure_gh_authenticated() succeeded, so in practice gh existed moments earlier.
Source
Thrown at crates/goose-cli/src/recipes/github_recipe.rs:263
/// Lists all available recipes from a GitHub repository
pub fn list_github_recipes(repo: &str) -> Result<Vec<RecipeInfo>> {
discover_github_recipes(repo)
}
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" {View on GitHub (pinned to 3810898a74)
Solutions
- Install/repair GitHub CLI and confirm `command -v gh && gh api repos/<owner>/<repo>/contents` works in goose's shell
- Check PATH stability of the environment (CI steps that strip PATH, direnv hooks) so gh stays resolvable for the whole goose invocation
- Re-run the command; a transient spawn failure (EAGAIN) clears when resource pressure drops
Defensive patterns
Strategy: validation
Validate before calling
# Exercise the exact API call goose will make gh api "repos/$GOOSE_RECIPE_GITHUB_REPO/contents" >/dev/null && echo "gh api OK"
Prevention
- Install and authenticate gh before using GitHub-backed recipe discovery
- Keep PATH stable across the whole goose invocation (avoid mid-run env swaps)
When it happens
Trigger: Command::new("gh").output() io error: gh not on PATH (would normally be caught earlier by the auth check), binary replaced/removed between the auth check and this call, exec permission/format error, or resource exhaustion at spawn.
Common situations: Same class as [126] but on the discovery path (`goose recipe list` style flows with GOOSE_RECIPE_GITHUB_REPO set): missing gh on minimal systems, PATH drift in wrappers, or concurrent brew/winget upgrades swapping the gh binary mid-run.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Failed to check directory contents: {}
- Failed to run `gh auth status`. Make sure you have `gh` inst
- Failed to run `gh auth login`
- Failed to clone repo: {}
- Failed to authenticate using GitHub CLI.
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/0c528dc078eac4ff.
Report an issue: GitHub.