{"record":{"id":"5bf678481cf16393","repo":"rust-lang/rust","slug":"extracted-directory-contains-more-than-one-dir","errorCode":null,"errorMessage":"extracted directory contains more than one dir","messagePattern":"extracted directory contains more than one dir","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/ci/docker/scripts/android-sdk-manager.py","lineNumber":190,"sourceCode":"            + MIRROR_BASE_DIR\n        )\n        downloaded = package.download(url)\n        # Extract the file in a temporary directory\n        extract_dir = tempfile.mkdtemp()\n        subprocess.run(\n            [\n                \"unzip\",\n                \"-q\",\n                downloaded,\n                \"-d\",\n                extract_dir,\n            ],\n            check=True,\n        )\n        # Figure out the prefix used in the zip\n        subdirs = [d for d in os.listdir(extract_dir) if not d.startswith(\".\")]\n        if len(subdirs) != 1:\n            raise RuntimeError(\"extracted directory contains more than one dir\")\n        # Move the extracted files in the proper directory\n        dest = os.path.join(args.dest, package.path.replace(\";\", \"/\"))\n        os.makedirs(\"/\".join(dest.split(\"/\")[:-1]), exist_ok=True)\n        os.rename(os.path.join(extract_dir, subdirs[0]), dest)\n        os.unlink(downloaded)\n\n\ndef cli():\n    parser = argparse.ArgumentParser()\n    subparsers = parser.add_subparsers()\n\n    add_to_lockfile = subparsers.add_parser(\"add-to-lockfile\")\n    add_to_lockfile.add_argument(\"lockfile\")\n    add_to_lockfile.add_argument(\"packages\", nargs=\"+\")\n    add_to_lockfile.set_defaults(func=cli_add_to_lockfile)\n\n    update_mirror = subparsers.add_parser(\"update-mirror\")\n    update_mirror.add_argument(\"lockfile\")","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/rust-lang/rust/blob/7088e4b63a9516ebfbfe2ab2d999cf01a528ac14/src/ci/docker/scripts/android-sdk-manager.py#L172-L208","documentation":"Raised by cli_install() in android-sdk-manager.py at line 190 after unzipping a downloaded package into a temporary directory. The code lists the top-level entries (excluding dotfiles) of the extracted directory and expects exactly one subdirectory (the package's internal prefix). If there are zero or more than one, the package layout is unexpected and the script cannot determine which directory to move to the destination.","triggerScenarios":"cli_install() unzips at line 177-186, then at line 188 computes subdirs = [d for d in os.listdir(extract_dir) if not d.startswith('.')]. At line 189, if len(subdirs) != 1, RuntimeError is raised at line 190. This fires when the zip archive contains multiple top-level directories or none.","commonSituations":"A corrupted or tampered package zip with an unexpected layout; the package format changed and now includes multiple top-level dirs; the zip was re-packaged incorrectly during mirroring; or a zip-bomb/extracted-with-extra-files scenario.","solutions":["Manually unzip the downloaded package and inspect its top-level structure to understand the anomaly.","Re-download the package and re-run install (the mirror may have served a wrong/corrupt file — also check error index 16 for hash mismatch).","If the package legitimately has a different layout, update the install logic to handle the correct prefix.","Verify the package was not extracted into a non-empty temp directory (tempfile.mkdtemp should always be clean, but verify)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Before moving extracted files, validate the zip structure\nimport os\ndef validate_single_subdir(extract_dir):\n    subdirs = [d for d in os.listdir(extract_dir) if not d.startswith('.')]\n    if len(subdirs) != 1:\n        print(f'Expected 1 top-level dir, found {len(subdirs)}: {subdirs}')\n        return None\n    return subdirs[0]","typeGuard":null,"tryCatchPattern":"try:\n    cli_install(args)\nexcept RuntimeError as e:\n    if 'more than one dir' in str(e):\n        print('Package zip has unexpected layout. Re-download or inspect manually.')\n    raise","preventionTips":["Verify package integrity before extraction (SHA-1 check catches most corrupt downloads).","Inspect unfamiliar package zips manually before running install.","Keep the lockfile current to avoid downloading packages with changed layouts."],"tags":["ci","android","sdk","extraction","zip","python"],"backgroundTag":null,"analyzedSha":"7088e4b63a9516ebfbfe2ab2d999cf01a528ac14","analyzedAt":"2026-08-10T14:17:03.603Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}