{"record":{"id":"a75eef25d1966cbd","repo":"reflex-dev/reflex","slug":"expected-one-directory-in-the-zip-found-subdirs","errorCode":null,"errorMessage":"Expected one directory in the zip, found {subdirs}","messagePattern":"Expected one directory in the zip, found (.+?)","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"reflex/utils/templates.py","lineNumber":168,"sourceCode":"\n    # Create a temp directory for the zip extraction.\n    try:\n        unzip_dir = Path(tempfile.mkdtemp())\n    except OSError as ose:\n        logger.error(f\"Failed to create temp directory for extracting zip: {ose}\")\n        raise SystemExit(1) from None\n\n    try:\n        zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)\n        # The zip file downloaded from github looks like:\n        # repo-name-branch/**/*, so we need to remove the top level directory.\n    except Exception as uze:\n        logger.error(f\"Failed to unzip the template: {uze}\")\n        raise SystemExit(1) from None\n\n    if len(subdirs := list(unzip_dir.iterdir())) != 1:\n        logger.error(f\"Expected one directory in the zip, found {subdirs}\")\n        raise SystemExit(1)\n\n    template_dir = unzip_dir / subdirs[0]\n    logger.debug(f\"Template folder is located at {template_dir}\")\n\n    # Move the rxconfig file here first.\n    path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE)\n    new_config = reload_config()\n\n    # Get the template app's name from rxconfig in case it is different than\n    # the source code repo name on github.\n    template_name = new_config.app_name\n\n    # Rewrite in place instead of regenerating from a stock template, so the\n    # template's own config (db_url, redis_url, plugins, etc.) is preserved.\n    rename_imports_and_app_name(constants.Config.FILE, template_name, app_name)\n    initialize_app_directory(\n        app_name,\n        template_name=template_name,","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/reflex-dev/reflex/blob/45b8ed5ab735f8a56bbb09a42384f030eb0208e7/reflex/utils/templates.py#L150-L186","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: template.zip contains\n#   __main__.py\n#   rxconfig.py\n#   my_app/\n\n# after: template.zip contains\n#   my-template-main/\n#     __main__.py\n#     rxconfig.py\n#     my_app/","handlingStrategy":"validation","validationCode":"import zipfile\n\ndef zip_layout_ok(zip_path: str) -> bool:\n    with zipfile.ZipFile(zip_path) as z:\n        tops = {n.split(\"/\", 1)[0] for n in z.namelist() if n.strip(\"/\")}\n        return len(tops) == 1","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["reflex","templates","zip","archive-layout"],"backgroundTag":"unexpected-archive-layout","analyzedSha":"45b8ed5ab735f8a56bbb09a42384f030eb0208e7","analyzedAt":"2026-08-28T19:25:27.644Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}