reflex-dev/reflex · error · SystemExit
Expected one directory in the zip, found {subdirs}
Error message
Expected one directory in the zip, found {subdirs} What it means
Raised by `create_config_init_app_from_remote_template` after extraction when the unzip directory does not contain exactly one top-level entry. Reflex expects GitHub-style archives shaped repo-name-branch/**, so it takes subdirs[0] as the template root; zero or multiple top-level entries make that ambiguous and abort.
Source
Thrown at reflex/utils/templates.py:168
# Create a temp directory for the zip extraction.
try:
unzip_dir = Path(tempfile.mkdtemp())
except OSError as ose:
logger.error(f"Failed to create temp directory for extracting zip: {ose}")
raise SystemExit(1) from None
try:
zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)
# The zip file downloaded from github looks like:
# repo-name-branch/**/*, so we need to remove the top level directory.
except Exception as uze:
logger.error(f"Failed to unzip the template: {uze}")
raise SystemExit(1) from None
if len(subdirs := list(unzip_dir.iterdir())) != 1:
logger.error(f"Expected one directory in the zip, found {subdirs}")
raise SystemExit(1)
template_dir = unzip_dir / subdirs[0]
logger.debug(f"Template folder is located at {template_dir}")
# Move the rxconfig file here first.
path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE)
new_config = reload_config()
# Get the template app's name from rxconfig in case it is different than
# the source code repo name on github.
template_name = new_config.app_name
# Rewrite in place instead of regenerating from a stock template, so the
# template's own config (db_url, redis_url, plugins, etc.) is preserved.
rename_imports_and_app_name(constants.Config.FILE, template_name, app_name)
initialize_app_directory(
app_name,
template_name=template_name,View on GitHub (pinned to 45b8ed5ab7)
Solutions
- Re-package the template zip so it has exactly one top-level directory containing the project (as GitHub's download-zip does).
- Remove extraneous top-level entries like __MACOSX/, .DS_Store, or loose files from the archive.
- If using GitHub, link the branch/tag archive URL so the repo-name-branch wrapper is present.
Example fix
# before: template.zip contains # __main__.py # rxconfig.py # my_app/ # after: template.zip contains # my-template-main/ # __main__.py # rxconfig.py # my_app/
Defensive patterns
Strategy: validation
Validate before calling
import zipfile
def zip_layout_ok(zip_path: str) -> bool:
with zipfile.ZipFile(zip_path) as z:
tops = {n.split("/", 1)[0] for n in z.namelist() if n.strip("/")}
return len(tops) == 1 Prevention
- Package custom template zips with exactly one top-level directory, mirroring GitHub's repo-name-branch layout.
- Exclude junk entries (__MACOSX, .DS_Store) when zipping templates.
When it happens
Trigger: A template zip with multiple top-level folders/files, or a flat zip whose contents are at the root (no wrapping directory); zips not produced by GitHub's archive endpoint.
Common situations: Hosting a custom template zip that was zipped 'from inside' the folder (flat layout) instead of wrapping the project in one top-level directory; archives containing __MACOSX or .DS_Store entries alongside the project folder.
Related errors
- Failed to unzip the template: {uze}
- Only {template_name=} should be provided, got {template_code
- For `{template_name}` template, `template_code_dir_name` and
- Failed to create temp directory for download: {ose}
- Failed to download the template: {he}
AI-assisted analysis of reflex-dev/reflex@45b8ed5ab7 (2026-08-28).
Data as JSON: /api/errors/a75eef25d1966cbd.
Report an issue: GitHub.