Hmbown/CodeWhale · error

download exceeds compressed size cap of bytes

Error message

download {url} exceeds compressed size cap of {compressed_cap} bytes

What it means

`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`.

Solutions

  1. Increase the `max_size` argument so the compressed cap (4x) accommodates the payload
  2. Publish/download a smaller, compressed (gzip) tarball of the skill
  3. Host the skill in smaller components and install them separately

Example fix

// before
let MAX: u64 = 1 * 1024 * 1024; // cap 4 MiB, payload is 6 MiB compressed
// after
let MAX: u64 = 4 * 1024 * 1024; // cap 16 MiB
Defensive patterns

Strategy: validation

Validate before calling

if expected_compressed_bytes > max_size * 4 {
    return Err(anyhow!("payload exceeds compressed cap; raise max_size"));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/3eb278316d3e42ee. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/skills/install.rs:1169

        .send()
        .await
        .with_context(|| format!("failed to GET {url}"))?;
    let status = resp.status();
    if !status.is_success() {
        if status == reqwest::StatusCode::NOT_FOUND {
            return Ok(DownloadAttempt::NotFound(status));
        }
        bail!("download {url} returned {status}");
    }
    // Soft cap on the *compressed* download — well above max_size to allow
    // for highly compressible payloads but still bounded.
    let compressed_cap = max_size.saturating_mul(4);
    let bytes = resp
        .bytes()
        .await
        .with_context(|| format!("failed to read body of {url}"))?;
    if (bytes.len() as u64) > compressed_cap {
        bail!("download {url} exceeds compressed size cap of {compressed_cap} bytes");
    }
    Ok(DownloadAttempt::Bytes(bytes.to_vec()))
}

struct StagedSkill {
    skill_name: String,
    staged_path: PathBuf,
}

/// Validate a tarball and extract it into `<skills_dir>/<name>.tmp/`.
fn stage_tarball(bytes: &[u8], skills_dir: &Path, max_size: u64) -> Result<StagedSkill> {
    fs::create_dir_all(skills_dir)
        .with_context(|| format!("failed to create skills directory {}", skills_dir.display()))?;

    // Two passes: first determine the skill name (and therefore the staged
    // dir) by finding the SKILL.md, then extract under that staged dir.
    // Both passes share the same archive bytes; we reset by wrapping fresh
    // decoders.

View on GitHub (pinned to 73e0f67d83)