aaif-goose/goose · error

Failed to get recipe content from GitHub

Error message

Failed to get recipe content from GitHub

What it means

The file-info JSON parsed but had no `content` string field, so the function falls through to this catch-all error. GitHub's contents API omits `content` when it cannot inline the payload: files larger than 1 MB (content/encoding returned empty), symlinks, and submodule entries. The recipe file exists, but this code path cannot retrieve its bytes.

Source

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

            .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))?;

        // Parse the recipe content
        let (recipe, _) = parse_recipe_content(&content, Some(format!("{}/{}", repo, dir_name)))?;

        return Ok(RecipeInfo {
            name: dir_name.to_string(),
            source: RecipeSource::GitHub,
            path: format!("{}/{}", repo, dir_name),
            title: Some(recipe.title),
            description: Some(recipe.description),
        });
    }

    Err(anyhow!("Failed to get recipe content from GitHub"))
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::Path;

    #[test]
    fn local_repo_path_includes_owner_to_avoid_collisions() {
        let parent = Path::new("goose-recipes");
        let first = get_local_repo_path(parent, "owner-one/shared").unwrap();
        let second = get_local_repo_path(parent, "owner-two/shared").unwrap();

        assert_ne!(first, second);
        assert_eq!(first, parent.join("owner-one__shared"));
        assert_eq!(second, parent.join("owner-two__shared"));
    }

View on GitHub (pinned to 3810898a74)

Solutions

  1. Check the `size` field of the file in the API response; if > 1048576, split the recipe (move data into separate extension files) or shrink it.
  2. If it is a symlink, replace it with a real file in the upstream repo.
  3. As a workaround, clone the repo locally and load the recipe from the filesystem path instead of via GitHub.

Example fix

# before
$ gh api repos/o/r/contents/big-recipe/recipe.yaml -q '.size'
2500000   # > 1MB, contents API returns no content
Error: Failed to get recipe content from GitHub

# after: split oversized recipe
# big-recipe/recipe.yaml  (keep < 1MB, reference extensions)
# big-recipe/extensions/*.yaml  (moved bulk content here)
Defensive patterns

Strategy: validation

Validate before calling

# guard the 1MB inline limit before loading
size=$(gh api "repos/${REPO}/contents/${DIR}/recipe.yaml" --jq '.size // 0')
[ "$size" -le 1048576 ] || { echo "recipe.yaml is ${size} bytes (>1MB); contents API omits content"; exit 1; }

Try / catch

if err.to_string() == "Failed to get recipe content from GitHub" {
    // content field absent: oversized file, symlink, or submodule entry
    suggest_clone_and_shrink();
}

Prevention

When it happens

Trigger: A recipe.yaml over 1 MB (huge embedded prompts, vendored data); recipe.yaml implemented as a symlink; the entry being a submodule reference rather than a regular file.

Common situations: Recipes that grew organically past the 1 MB contents-API inline limit — the earlier directory listing works, then content extraction silently degrades to this generic message.

Related errors


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