zeroclaw-labs/zeroclaw · error · anyhow::Error
plugin archive exceeds extracted size limit of {max_extracte
Error message
plugin archive exceeds extracted size limit of {max_extracted_bytes} bytes What it means
Zip-bomb defense part 1: before writing each file, extract_zip_safe_with_limit sums the entry's declared size (file.size(), saturating_add) against max_extracted_bytes and bails if the cumulative extracted total would exceed the cap. It relies on the zip header's declared uncompressed size.
Source
Thrown at src/plugin_registry.rs:192
where
R: Read + Seek,
{
let mut archive = zip::ZipArchive::new(reader)?;
std::fs::create_dir_all(dest)?;
let mut extracted_bytes = 0_u64;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let enclosed = enclosed_zip_path(file.name(), &file)?;
let out_path = dest.join(enclosed);
if file.is_dir() {
std::fs::create_dir_all(&out_path)?;
continue;
}
let Some(parent) = out_path.parent() else {
bail!("plugin archive entry has no parent: {}", file.name());
};
if extracted_bytes.saturating_add(file.size()) > max_extracted_bytes {
bail!("plugin archive exceeds extracted size limit of {max_extracted_bytes} bytes");
}
std::fs::create_dir_all(parent)?;
let mut out = File::create(&out_path)?;
copy_zip_entry_capped(
&mut file,
&mut out,
&mut extracted_bytes,
max_extracted_bytes,
)?;
}
Ok(dest.to_path_buf())
}
fn enclosed_zip_path<R>(raw_name: &str, file: &zip::read::ZipFile<'_, R>) -> Result<PathBuf>
where
R: Read,
{
if is_unsafe_zip_entry_name(raw_name) {View on GitHub (pinned to 88bb9c8533)
Solutions
- Slim the plugin: move large assets out of the zip and download them at runtime from their own source
- If assets are legitimately needed, compress less aggressively irrelevant — the cap is on extracted bytes, so the only fix is fewer/smaller files
- Verify with `unzip -v plugin.zip` which entries have the largest uncompressed sizes and prune them
- Raise max_extracted_bytes only if you control the build and accept the disk-exhaustion risk
Example fix
# before: bundle includes 5GB model.bin -> extracted total over cap # after: ship plugin without the asset # manifest fetches it at first run from https://models.example/p.model zip -r p.zip . -x 'assets/model.bin'
Defensive patterns
Strategy: validation
Validate before calling
// Sum declared uncompressed sizes before extracting (same policy as the guard):
let total: u64 = (0..archive.len()).map(|i| archive.by_index(i).unwrap().size()).sum();
if total > max_extracted_bytes { anyhow::bail!("archive would exceed extraction cap"); } Try / catch
// On the extracted-size bail, inspect `unzip -v` for the largest entries and // republish without them; do not raise the cap reflexively.
Prevention
- Keep heavy assets out of plugin zips (fetch at runtime instead)
- Check uncompressed totals in the release pipeline
- Set explicit asset budgets per plugin
When it happens
Trigger: Installing a plugin whose archive declares entries that individually or cumulatively exceed the extracted-size cap — e.g. a highly compressed archive that expands to gigabytes, or simply a plugin bundling oversized assets. The companion streaming check (error 1417) catches lying headers.
Common situations: Plugin bundles large model files, datasets, or static assets; upstream lowered the extraction cap in a fork; a crafted zip bomb aimed at exhausting disk during install.
Related errors
- plugin archive exceeds maximum size of {MAX_PLUGIN_ZIP_BYTES
- plugin archive exceeds maximum size of {max_bytes} bytes
- plugin archive contains unsafe path: {raw_name}
- plugin archive sha256 mismatch
- plugin archive entry has no parent: {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/463bcb14bbeb8a48.
Report an issue: GitHub.