aaif-goose/goose · error

Failed to access directory: {}

Error message

Failed to access directory: {}

What it means

check_github_directory_for_recipe treats a non-zero exit of `gh api repos/<repo>/contents/<dir>` as this error, naming the directory that failed (crates/goose-cli/src/recipes/github_recipe.rs:305-307). gh's stderr with the concrete HTTP reason appears above it. Common causes: the directory does not exist (repo changed since the parent listing), is a file not a dir, no access, or rate limiting during discovery.

Source

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

        }
    }

    Ok(recipes)
}

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

    // Check directory contents for recipe files
    let output = Command::new("gh")
        .args(["api", &format!("repos/{}/contents/{}", repo, dir_name)])
        .set_no_window()
        .output()
        .map_err(|e| anyhow!("Failed to check directory contents: {}", e))?;

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

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

    if let Some(items) = contents.as_array() {
        for item in items {
            if let Some(name) = item.get("name").and_then(|n| n.as_str()) {
                if RECIPE_FILE_EXTENSIONS
                    .iter()
                    .any(|ext| name == format!("recipe.{}", ext))
                {
                    // Found a recipe file, get its content
                    return get_github_recipe_info(repo, dir_name, name);
                }
            }
        }
    }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Check the directory manually: `gh api repos/<owner>/<repo>/contents/<dir-name>` — the returned status explains whether it is 404, 403, or encoding-related
  2. Rename repo directories to URL-safe names (lowercase, hyphens) if special characters are involved
  3. If rate-limited during discovery of a large repo, wait for reset or authenticate with a higher-limit token; re-run after the repo layout settles
Defensive patterns

Strategy: try-catch

Validate before calling

# Validate each candidate directory is API-addressable before discovery
gh repo view "$GOOSE_RECIPE_GITHUB_REPO" --json name >/dev/null
gh api "repos/$GOOSE_RECIPE_GITHUB_REPO/contents" --jq '.[] | select(.type=="dir") | .name' \
  | while read -r d; do gh api "repos/$GOOSE_RECIPE_GITHUB_REPO/contents/$d" >/dev/null || echo "bad dir: $d"; done

Try / catch

// Rust: skip directories that fail to probe instead of aborting all discovery
match check_github_directory_for_recipe(repo, name) {
    Ok(info) => recipes.push(info),
    Err(e) => tracing::warn!("skipping directory {name}: {e}"),
}

Prevention

When it happens

Trigger: Repo layout changes between the contents listing and the per-directory probe; symlinks/submodules the API lists but cannot enumerate; private/protected paths the token cannot read; secondary rate limits when probing many directories quickly.

Common situations: Recipe repo being edited while goose lists it, large repos triggering GitHub rate limits during discovery, or names with characters needing URL encoding (spaces, '#') that break the naive path concatenation in the API URL.

Related errors


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