aaif-goose/goose · error

Failed to access recipe file: {}/{}

Error message

Failed to access recipe file: {}/{}

What it means

`gh api repos/{repo}/contents/{dir}/{recipe_filename}` ran but exited non-zero while fetching the recipe file itself. This is an HTTP-level failure surfaced through gh: 404 when the path is wrong (case-sensitive, or the file was renamed between the listing and the fetch), 401/403 for auth or permission problems on private repos, and 403 for primary/secondary rate limits.

Source

Thrown at crates/goose-cli/src/recipes/github_recipe.rs:344

    Err(anyhow!("No recipe file found in directory: {}", dir_name))
}

fn get_github_recipe_info(repo: &str, dir_name: &str, recipe_filename: &str) -> Result<RecipeInfo> {
    use serde_json::Value;
    use std::process::Command;

    // Get the recipe file content
    let output = Command::new("gh")
        .args([
            "api",
            &format!("repos/{}/contents/{}/{}", repo, dir_name, recipe_filename),
        ])
        .set_no_window()
        .output()
        .map_err(|e| anyhow!("Failed to get recipe file content: {}", e))?;

    if !output.status.success() {
        return Err(anyhow!(
            "Failed to access recipe file: {}/{}",
            dir_name,
            recipe_filename
        ));
    }

    let file_info: Value = serde_json::from_slice(&output.stdout)
        .map_err(|e| anyhow!("Failed to parse file info: {}", e))?;

    if let Some(content_b64) = file_info.get("content").and_then(|c| c.as_str()) {
        // Decode base64 content
        use base64::{engine::general_purpose, Engine as _};
        let content_bytes = general_purpose::STANDARD
            .decode(content_b64.replace('\n', ""))
            .map_err(|e| anyhow!("Failed to decode base64 content: {}", e))?;

        let content = String::from_utf8(content_bytes)
            .map_err(|e| anyhow!("Failed to convert content to string: {}", e))?;

View on GitHub (pinned to 3810898a74)

Solutions

  1. Run the exact URL manually: `gh api "repos/{repo}/contents/{dir}/{file}"` and read the HTTP status it reports.
  2. If rate-limited (403 with rate limit message), wait for the reset window or authenticate via `gh auth login` to raise the limit.
  3. If 404, verify the file name and its casing against the directory listing.
  4. If 401/403 on a private repo, re-authenticate or grant the token repo access.

Example fix

# before
$ goose recipe info owner/private-repo/my-recipe
Error: Failed to access recipe file: my-recipe/recipe.yaml

# after
$ gh auth status   # shows token lacks repo scope
$ gh auth refresh -h github.com -s repo
$ goose recipe info owner/private-repo/my-recipe
Defensive patterns

Strategy: retry

Validate before calling

# confirm the exact path resolves before goose fetches it (case-sensitive)
gh api "repos/${REPO}/contents/${DIR}/recipe.yaml" --jq '.name'

Try / catch

for attempt in 0..3 {
    match fetch_recipe_file(repo, dir, file) {
        Err(e) if e.to_string().contains("Failed to access recipe file") && attempt < 2 => {
            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await; // rate-limit backoff
        }
        r => break r,
    }
}

Prevention

When it happens

Trigger: Recipe file renamed or deleted between the directory listing and this second API call (race); unauthenticated gh hitting GitHub's rate limit while listing many recipes; accessing a private repo without an authorized token; wrong-case path segments on case-sensitive APIs.

Common situations: Scanning large recipe collections until the unauthenticated rate limit trips; upstream repos moving files; tokens lacking repo scope for private recipe repos.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/4d2a557cb3f13acd. Report an issue: GitHub.