aaif-goose/goose · error

Failed to decode base64 content: {}

Error message

Failed to decode base64 content: {}

What it means

The recipe file's `content` field from GitHub's contents API is base64, but base64::decode failed after stripping only '\n'. Two concrete causes: the response contains '\r\n' line breaks (the code strips '\n' but leaves '\r', an invalid base64 character), or the `encoding` field is "none" (files 1–100 MB), where `content` holds non-base64 data.

Source

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

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

        // 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"))
}

View on GitHub (pinned to 3810898a74)

Solutions

  1. Check the file size via the listing's `size` field; if it exceeds 1048576 bytes, shrink the recipe or fetch it another way — the contents API will not deliver usable base64.
  2. If size is small, inspect the raw content field for '\r' characters and strip them too.
  3. Prefer fetching raw content (`gh api -H 'Accept: application/vnd.github.raw' ...`) which bypasses base64 entirely, and suggest that upstream adopt it.

Example fix

// before (crates/goose-cli/src/recipes/github_recipe.rs)
let content_bytes = general_purpose::STANDARD
    .decode(content_b64.replace('\n', ""))
    .map_err(|e| anyhow!("Failed to decode base64 content: {}", e))?;

// after: strip CR as well and reject encoding != "base64"
if file_info.get("encoding").and_then(|e| e.as_str()) != Some("base64") {
    return Err(anyhow!("File too large for contents API (encoding != base64)"));
}
let content_bytes = general_purpose::STANDARD
    .decode(content_b64.replace(['\n', '\r'], ""))
    .map_err(|e| anyhow!("Failed to decode base64 content: {}", e))?;
Defensive patterns

Strategy: validation

Validate before calling

# reject files the contents API can't inline as base64
size=$(gh api "repos/${REPO}/contents/${DIR}/recipe.yaml" --jq '.size')
enc=$(gh api "repos/${REPO}/contents/${DIR}/recipe.yaml" --jq '.encoding')
[ "$size" -lt 1048576 ] && [ "$enc" = "base64" ] || echo "too large / not base64 — fetch raw instead"

Try / catch

match decode_content(&file_info) {
    Err(e) if e.to_string().contains("Failed to decode base64") => {
        // fall back to raw media-type fetch
        fetch_raw(repo, dir, file).await
    }
    r => r,
}

Prevention

When it happens

Trigger: A base64 payload wrapped with CRLF line endings (proxies, some gh transports) leaving stray '\r' characters; a recipe file larger than 1 MB where GitHub returns encoding="none" so content is not base64 at all.

Common situations: Recipe.yaml files that grew past 1 MB (embedded blobs, giant prompts); HTTP intermediaries normalizing line endings to CRLF.

Understand the failure class

Related errors


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