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

  1. Re-package the template zip so it has exactly one top-level directory containing the project (as GitHub's download-zip does).
  2. Remove extraneous top-level entries like __MACOSX/, .DS_Store, or loose files from the archive.
  3. 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

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


AI-assisted analysis of reflex-dev/reflex@45b8ed5ab7 (2026-08-28). Data as JSON: /api/errors/a75eef25d1966cbd. Report an issue: GitHub.