headroomlabs-ai/headroom · error · SystemExit
expected one sdist root directory, found {sorted(roots)}
Error message
expected one sdist root directory, found {sorted(roots)} What it means
verify_sdist_license_files() opens the sdist tarball and requires every member to sit under exactly one top-level root directory (the standard `<name>-<version>/` layout sdists use). Multiple roots mean stray top-level files leaked into the archive; zero roots means an effectively empty archive.
Source
Thrown at scripts/build_python_release_smoke.py:125
raise SystemExit(
f"{wheel.name} should contain exactly one dist-info/METADATA, "
f"found {len(metadata_members)}"
)
metadata = archive.read(metadata_members[0]).decode("utf-8")
expected_line = f"Version: {expected_version}"
if expected_line not in metadata.splitlines():
raise SystemExit(f"{wheel.name} metadata missing {expected_line!r}")
print(f"wheel metadata OK: {wheel.name} contains {native_members[0]}")
def verify_sdist_license_files(sdist: Path) -> None:
with tarfile.open(sdist, "r:gz") as archive:
names = set(archive.getnames())
roots = {name.split("/", 1)[0] for name in names if "/" in name}
if len(roots) != 1:
raise SystemExit(f"expected one sdist root directory, found {sorted(roots)}")
root = roots.pop()
pkg_info_path = f"{root}/PKG-INFO"
member = archive.getmember(pkg_info_path)
fh = archive.extractfile(member)
if fh is None:
raise SystemExit(f"could not read {pkg_info_path} from {sdist.name}")
pkg_info = fh.read().decode("utf-8")
declared = []
for line in pkg_info.splitlines():
if not line.strip():
break
if line.startswith("License-File:"):
declared.append(line.split(":", 1)[1].strip())
if not declared:
raise SystemExit(f"{sdist.name} declares no License-File entries")View on GitHub (pinned to 322425c43b)
Solutions
- Inspect the archive roots: `tar -tzf file.tar.gz | cut -d/ -f1 | sort -u`.
- Move or exclude the stray top-level files (maturin sdist includes/excludes in pyproject.toml) so everything nests under the single `name-version/` root.
- Rebuild the sdist in a clean checkout to rule out untracked local files.
Defensive patterns
Strategy: validation
Validate before calling
import tarfile
def sdist_roots(sdist_path: str) -> set[str]:
with tarfile.open(sdist_path, "r:gz") as t:
return {n.split("/", 1)[0] for n in t.getnames() if "/" in n}
# assert len(sdist_roots(p)) == 1 before deeper verification Prevention
- Keep generated artifacts out of the repo root or exclude them from sdist inclusion rules.
- Review [tool.maturin] include/exclude globs whenever top-level files are added.
- Build sdists from a clean export (git archive) when in doubt.
When it happens
Trigger: maturin including extra top-level files in the sdist (stray files at repo root picked up by include patterns); an sdist built with a non-standard packer; an empty or corrupted tarball.
Common situations: Adding generated files at the repo root that maturin's sdist inclusion rules pick up; changing pyproject `[tool.maturin]` include/exclude globs; MANIFEST-style configuration drift.
Related errors
- could not read {pkg_info_path} from {sdist.name}
- {sdist.name} declares no License-File entries
- {sdist.name} declares License-File entries missing from tarb
- {tool}: distributed via PyPI only; `pip install headroom-ai`
- archive did not contain expected member {member!r}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/8f95b372a4ade7a8.
Report an issue: GitHub.