zeroclaw-labs/zeroclaw · error · anyhow::Error

plugin archive exceeds maximum size of {max_bytes} bytes

Error message

plugin archive exceeds maximum size of {max_bytes} bytes

What it means

The streaming backstop of the archive size cap: while reading the response body chunk by chunk, append_chunk_capped bails as soon as accumulated bytes (plus the incoming chunk) would exceed max_bytes. It catches servers that lie about (or omit) Content-Length, which the header check (error 1409) cannot see.

Source

Thrown at src/plugin_registry.rs:144

    }
    Ok(bytes)
}

pub(crate) fn collect_capped_chunks<I>(chunks: I, max_bytes: usize) -> Result<Vec<u8>>
where
    I: IntoIterator<Item = Result<Vec<u8>>>,
{
    let mut bytes = Vec::new();
    for chunk in chunks {
        let chunk = chunk?;
        append_chunk_capped(&mut bytes, &chunk, max_bytes)?;
    }
    Ok(bytes)
}

fn append_chunk_capped(bytes: &mut Vec<u8>, chunk: &[u8], max_bytes: usize) -> Result<()> {
    if bytes.len().saturating_add(chunk.len()) > max_bytes {
        bail!("plugin archive exceeds maximum size of {max_bytes} bytes");
    }
    bytes.extend_from_slice(chunk);
    Ok(())
}

fn verify_sha256_if_present(bytes: &[u8], expected: Option<&str>) -> Result<()> {
    let Some(expected) = expected else {
        return Ok(());
    };
    let expected = expected.strip_prefix("sha256:").unwrap_or(expected);
    let actual = hex::encode(Sha256::digest(bytes));
    if !actual.eq_ignore_ascii_case(expected) {
        bail!("plugin archive sha256 mismatch");
    }
    Ok(())
}

pub(crate) fn extract_zip_safe<R>(reader: R, dest: &Path) -> Result<PathBuf>

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Treat it as the cap working as designed: the remote is sending more bytes than it declared (or nothing at all)
  2. Fetch the URL manually (curl -L <url> -o /dev/null -w '%{size_download}') to see the true size
  3. Re-publish a smaller artifact, or point the entry at a host that reports accurate Content-Length so the header check can fail fast
  4. Raise the limit only if the artifact legitimately needs to be larger and you accept the memory implications

Example fix

# before: server sends chunked body of 2x the cap with no Content-Length
# after: serve the artifact with an accurate header
curl -I https://cdn/p.zip   # must show content-length < MAX_PLUGIN_ZIP_BYTES
Defensive patterns

Strategy: try-catch

Try / catch

// Stream the body yourself with the same saturating cap if you need custom UX:
let mut total = 0usize;
while let Some(chunk) = resp.chunk().await? {
    total = total.saturating_add(chunk.len());
    if total > MAX_PLUGIN_ZIP_BYTES { return Err(anyhow::anyhow!("over cap")); }
    buf.extend_from_slice(&chunk);
}

Prevention

When it happens

Trigger: Downloading a plugin archive where Content-Length is missing or understated, and the actual streamed body exceeds MAX_PLUGIN_ZIP_BYTES. Reached from download_archive_bytes' chunk loop (and reusable via collect_capped_chunks).

Common situations: Chunked transfer-encoding with no Content-Length from the artifact host; a proxy/CDN rewriting headers; a malicious or misconfigured server deliberately under-reporting size to slip a large payload through.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/8893b3583b4af0da. Report an issue: GitHub.