rust-lang/rust · error · RuntimeError
extracted directory contains more than one dir
Error message
extracted directory contains more than one dir
What it means
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.
Source
Thrown at src/ci/docker/scripts/android-sdk-manager.py:190
+ MIRROR_BASE_DIR
)
downloaded = package.download(url)
# Extract the file in a temporary directory
extract_dir = tempfile.mkdtemp()
subprocess.run(
[
"unzip",
"-q",
downloaded,
"-d",
extract_dir,
],
check=True,
)
# Figure out the prefix used in the zip
subdirs = [d for d in os.listdir(extract_dir) if not d.startswith(".")]
if len(subdirs) != 1:
raise RuntimeError("extracted directory contains more than one dir")
# Move the extracted files in the proper directory
dest = os.path.join(args.dest, package.path.replace(";", "/"))
os.makedirs("/".join(dest.split("/")[:-1]), exist_ok=True)
os.rename(os.path.join(extract_dir, subdirs[0]), dest)
os.unlink(downloaded)
def cli():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
add_to_lockfile = subparsers.add_parser("add-to-lockfile")
add_to_lockfile.add_argument("lockfile")
add_to_lockfile.add_argument("packages", nargs="+")
add_to_lockfile.set_defaults(func=cli_add_to_lockfile)
update_mirror = subparsers.add_parser("update-mirror")
update_mirror.add_argument("lockfile")View on GitHub (pinned to 7088e4b63a)
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).
Defensive patterns
Strategy: validation
Validate before calling
# Before moving extracted files, validate the zip structure
import os
def validate_single_subdir(extract_dir):
subdirs = [d for d in os.listdir(extract_dir) if not d.startswith('.')]
if len(subdirs) != 1:
print(f'Expected 1 top-level dir, found {len(subdirs)}: {subdirs}')
return None
return subdirs[0] Try / catch
try:
cli_install(args)
except RuntimeError as e:
if 'more than one dir' in str(e):
print('Package zip has unexpected layout. Re-download or inspect manually.')
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- hash mismatch for package {path}: {sha1} vs {sha1_known} (kn
- package not found: {name}
- Unrecognized target triple {triple}
- src/stage0 doesn't contain a checksum for {}. Pre-built arti
- failed verification
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/5bf678481cf16393.
Report an issue: GitHub.