{"record":{"id":"3eb278316d3e42ee","repo":"Hmbown/CodeWhale","slug":"download-url-exceeds-compressed-size-cap-of-com","errorCode":null,"errorMessage":"download {url} exceeds compressed size cap of {compressed_cap} bytes","messagePattern":"download (.+?) exceeds compressed size cap of (.+?) bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/install.rs","lineNumber":1169,"sourceCode":"        .send()\n        .await\n        .with_context(|| format!(\"failed to GET {url}\"))?;\n    let status = resp.status();\n    if !status.is_success() {\n        if status == reqwest::StatusCode::NOT_FOUND {\n            return Ok(DownloadAttempt::NotFound(status));\n        }\n        bail!(\"download {url} returned {status}\");\n    }\n    // Soft cap on the *compressed* download — well above max_size to allow\n    // for highly compressible payloads but still bounded.\n    let compressed_cap = max_size.saturating_mul(4);\n    let bytes = resp\n        .bytes()\n        .await\n        .with_context(|| format!(\"failed to read body of {url}\"))?;\n    if (bytes.len() as u64) > compressed_cap {\n        bail!(\"download {url} exceeds compressed size cap of {compressed_cap} bytes\");\n    }\n    Ok(DownloadAttempt::Bytes(bytes.to_vec()))\n}\n\nstruct StagedSkill {\n    skill_name: String,\n    staged_path: PathBuf,\n}\n\n/// Validate a tarball and extract it into `<skills_dir>/<name>.tmp/`.\nfn stage_tarball(bytes: &[u8], skills_dir: &Path, max_size: u64) -> Result<StagedSkill> {\n    fs::create_dir_all(skills_dir)\n        .with_context(|| format!(\"failed to create skills directory {}\", skills_dir.display()))?;\n\n    // Two passes: first determine the skill name (and therefore the staged\n    // dir) by finding the SKILL.md, then extract under that staged dir.\n    // Both passes share the same archive bytes; we reset by wrapping fresh\n    // decoders.","sourceCodeStart":1151,"sourceCodeEnd":1187,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/skills/install.rs#L1151-L1187","documentation":"`download_with_cap` enforces a soft cap on the compressed body of `max_size * 4` bytes before decompression, bounding memory use. A response larger than this cap is rejected rather than fully buffered, even if it would decompress under `max_size`.","triggerScenarios":"Downloading a skill tarball whose compressed body exceeds `4 * max_size` bytes — very large archives, or a small `max_size` argument relative to the payload.","commonSituations":"Registry entry pointing at a huge archive; caller passing an artificially low `max_size`; mirrors serving uncompressed (already-large) tarballs; a compromised/hostile URL serving gigabytes.","solutions":["Increase the `max_size` argument so the compressed cap (4x) accommodates the payload","Publish/download a smaller, compressed (gzip) tarball of the skill","Host the skill in smaller components and install them separately"],"exampleFix":"// before\nlet MAX: u64 = 1 * 1024 * 1024; // cap 4 MiB, payload is 6 MiB compressed\n// after\nlet MAX: u64 = 4 * 1024 * 1024; // cap 16 MiB","handlingStrategy":"validation","validationCode":"if expected_compressed_bytes > max_size * 4 {\n    return Err(anyhow!(\"payload exceeds compressed cap; raise max_size\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set max_size to cover the largest expected compressed payload","Keep published skill tarballs small and gzip-compressed","Check Content-Length before download when available"],"tags":["download","size-limit","payload"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}