{"record":{"id":"5c78ad4a113b4483","repo":"aaif-goose/goose","slug":"failed-to-decode-base64-content","errorCode":null,"errorMessage":"Failed to decode base64 content: {}","messagePattern":"Failed to decode base64 content: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-cli/src/recipes/github_recipe.rs","lineNumber":359,"sourceCode":"        .map_err(|e| anyhow!(\"Failed to get recipe file content: {}\", e))?;\n\n    if !output.status.success() {\n        return Err(anyhow!(\n            \"Failed to access recipe file: {}/{}\",\n            dir_name,\n            recipe_filename\n        ));\n    }\n\n    let file_info: Value = serde_json::from_slice(&output.stdout)\n        .map_err(|e| anyhow!(\"Failed to parse file info: {}\", e))?;\n\n    if let Some(content_b64) = file_info.get(\"content\").and_then(|c| c.as_str()) {\n        // Decode base64 content\n        use base64::{engine::general_purpose, Engine as _};\n        let content_bytes = general_purpose::STANDARD\n            .decode(content_b64.replace('\\n', \"\"))\n            .map_err(|e| anyhow!(\"Failed to decode base64 content: {}\", e))?;\n\n        let content = String::from_utf8(content_bytes)\n            .map_err(|e| anyhow!(\"Failed to convert content to string: {}\", e))?;\n\n        // Parse the recipe content\n        let (recipe, _) = parse_recipe_content(&content, Some(format!(\"{}/{}\", repo, dir_name)))?;\n\n        return Ok(RecipeInfo {\n            name: dir_name.to_string(),\n            source: RecipeSource::GitHub,\n            path: format!(\"{}/{}\", repo, dir_name),\n            title: Some(recipe.title),\n            description: Some(recipe.description),\n        });\n    }\n\n    Err(anyhow!(\"Failed to get recipe content from GitHub\"))\n}","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-cli/src/recipes/github_recipe.rs#L341-L377","documentation":"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.","triggerScenarios":"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.","commonSituations":"Recipe.yaml files that grew past 1 MB (embedded blobs, giant prompts); HTTP intermediaries normalizing line endings to CRLF.","solutions":["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.","If size is small, inspect the raw content field for '\\r' characters and strip them too.","Prefer fetching raw content (`gh api -H 'Accept: application/vnd.github.raw' ...`) which bypasses base64 entirely, and suggest that upstream adopt it."],"exampleFix":"// before (crates/goose-cli/src/recipes/github_recipe.rs)\nlet content_bytes = general_purpose::STANDARD\n    .decode(content_b64.replace('\\n', \"\"))\n    .map_err(|e| anyhow!(\"Failed to decode base64 content: {}\", e))?;\n\n// after: strip CR as well and reject encoding != \"base64\"\nif file_info.get(\"encoding\").and_then(|e| e.as_str()) != Some(\"base64\") {\n    return Err(anyhow!(\"File too large for contents API (encoding != base64)\"));\n}\nlet content_bytes = general_purpose::STANDARD\n    .decode(content_b64.replace(['\\n', '\\r'], \"\"))\n    .map_err(|e| anyhow!(\"Failed to decode base64 content: {}\", e))?;","handlingStrategy":"validation","validationCode":"# reject files the contents API can't inline as base64\nsize=$(gh api \"repos/${REPO}/contents/${DIR}/recipe.yaml\" --jq '.size')\nenc=$(gh api \"repos/${REPO}/contents/${DIR}/recipe.yaml\" --jq '.encoding')\n[ \"$size\" -lt 1048576 ] && [ \"$enc\" = \"base64\" ] || echo \"too large / not base64 — fetch raw instead\"","typeGuard":null,"tryCatchPattern":"match decode_content(&file_info) {\n    Err(e) if e.to_string().contains(\"Failed to decode base64\") => {\n        // fall back to raw media-type fetch\n        fetch_raw(repo, dir, file).await\n    }\n    r => r,\n}","preventionTips":["Keep recipe files well under 1 MB.","Prefer `gh api -H 'Accept: application/vnd.github.raw'` in your own fetchers to skip base64.","Normalize CRLF in base64 payloads before decoding."],"tags":["base64","encoding","github","size-limit","recipes"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}