crewAIInc/crewAI · error · SystemExit
Downloaded archive is empty.
Error message
Downloaded archive is empty.
What it means
Raised by RemoteTemplateManager._extract_zip() when the downloaded GitHub zipball opens as a valid zip but contains zero entries (zf.namelist() is empty). It is a data-integrity guard before any files are written. The CLI prints the red message and exits 1; an empty destination directory may already have been created by a prior branch but no content is extracted.
Source
Thrown at lib/cli/src/crewai_cli/remote_template/main.py:225
"""Download the default branch zipball for a repo."""
url = f"{GITHUB_API_BASE}/repos/{GITHUB_ORG}/{repo_name}/zipball"
try:
response = httpx.get(url, follow_redirects=True, timeout=60)
response.raise_for_status()
except httpx.HTTPError as e:
click.secho(f"Failed to download template: {e}", fg="red")
raise SystemExit(1) from e
return response.content
def _extract_zip(self, zip_bytes: bytes, dest: str) -> None:
"""Extract a GitHub zipball into dest, stripping the top-level directory."""
with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf:
# GitHub zipballs have a single top-level dir like 'crewAIInc-fde-template_xxx-<sha>/'
members = zf.namelist()
if not members:
click.secho("Downloaded archive is empty.", fg="red")
raise SystemExit(1)
top_dir = members[0].split("/")[0] + "/"
os.makedirs(dest, exist_ok=True)
for member in members:
if member == top_dir or not member.startswith(top_dir):
continue
relative_path = member[len(top_dir) :]
if not relative_path:
continue
target = os.path.realpath(os.path.join(dest, relative_path))
if not target.startswith(
os.path.realpath(dest) + os.sep
) and target != os.path.realpath(dest):
continueView on GitHub (pinned to 754d7323be)
Solutions
- Re-run `crewai template add <name>` — an empty archive is almost always a corrupted transfer.
- If it repeats, download the zip manually (curl -L from github.com/crewAIInc/<repo>/archive/...) and verify `unzip -l` shows files.
- Bypass or configure the intercepting proxy/AV if manual downloads are also empty.
- Report upstream if the repo's default branch is genuinely empty on GitHub.
Defensive patterns
Strategy: validation
Validate before calling
import io, zipfile
def is_usable_zipball(data: bytes) -> bool:
try:
with zipfile.ZipFile(io.BytesIO(data)) as zf:
return len(zf.namelist()) > 0
except zipfile.BadZipFile:
return False Prevention
- When downloading templates yourself, verify the zip has entries before extracting.
- Compare the downloaded size with GitHub's reported size to detect truncated transfers.
- An empty archive means corrupted transfer — re-download instead of investigating your local code.
When it happens
Trigger: The zipball HTTP body was truncated or replaced by an empty/intercepted 200 response (transparent proxy, captive portal) that still decompresses as a zip; extremely rare GitHub-side artifact corruption. Genuine GitHub zipballs always contain at least the top-level directory entry.
Common situations: Corporate proxies mangling binary downloads; anti-virus stripping archive contents; flaky connections yielding a short body that happens to parse as an empty zip.
Related errors
- Failed to download template: {e}
- project_name is required to update a ZIP deployment
- Template '{name}' not found.
- Failed to fetch templates from GitHub: {e}
- Blocked path traversal attempt: {member!r}
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/ea8dc519a107f4f8.
Report an issue: GitHub.