rust-lang/cargo · error

invalid tarball downloaded, contains an entry at

Error message

invalid tarball downloaded, contains an entry at {entry_path:?} with invalid type {t:?}

What it means

During unpacking, after the prefix check passes, Cargo only permits `Regular` and `Directory` tar entry types. Any other type (symlink, hardlink, char/block device, fifo) is rejected to prevent symlinks escaping the unpack dir or device-file attacks. The error names the offending entry path and the invalid type.

Solutions

  1. Re-package the crate with `cargo package` (which excludes symlinks), then re-publish.
  2. Replace the offending tarball in cache: `rm ~/.cargo/registry/cache/<index>/<pkg>-<ver>.crate` and refetch from a trusted registry.
  3. Remove symlinks from the crate's source tree before packaging.
  4. Audit the registry for crates with non-regular entries.

Example fix

# before: crate contains a symlink
$ tar tvf mycrate-1.0.0.crate
lrwxrwxrwx ... mycrate-1.0.0/lib/orig

# after: repackage without symlinks
$ rm crate-src/lib/orig        # or copy the target file in
$ cargo package
# now tar entries are Regular/Directory only
Defensive patterns

Strategy: validation

Validate before calling

fn validate_tarball_entry_types(tar_path: &Path) -> Result<(), anyhow::Error> {
    let mut a = tar::Archive::new(std::fs::File::open(tar_path)?);
    for e in a.entries()? {
        let e = e?;
        match e.header().entry_type() {
            tar::EntryType::Regular | tar::EntryType::Directory => {},
            t => anyhow::bail!("disallowed entry type {:?} at {:?}", t, e.path()?),
        }
    }
    Ok(())
}

Type guard

fn tarball_has_only_regular_entries(path: &std::path::Path) -> bool {
    std::fs::File::open(path).ok()
        .and_then(|f| tar::Archive::new(f).entries().ok())
        .map_or(false, |mut es| es.all(|e| e.map_or(false, |e| matches!(e.header().entry_type(), tar::EntryType::Regular | tar::EntryType::Directory))))
}

Try / catch

match entry.header().entry_type() {
    EntryType::Regular | EntryType::Directory => {},
    _ => {
        // recover by re-fetching from a trusted registry
        refetch_crate(pkg)?;
        return unpack(...);
    }
}

Prevention

When it happens

Trigger: `entry.header().entry_type()` is neither `Regular` nor `Directory`. Most commonly a `Symlink` entry, but also hardlinks, character/block devices, fifos, or contiguous-file types. Caused by a tarball containing links/devices that Cargo forbids.

Common situations: A crate packaged with symlinks (e.g. by a non-cargo packager or `tar` preserving symlinks from the source tree); a tampered tarball embedding device files; registry mirror re-packaging that introduced links; cross-platform packaging where symlinks were used on Linux.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/4986b5cef2b3f028. Report an issue: GitHub.

Appendix: source

Thrown at src/sources/registry/mod.rs:1008

                continue;
            }
        } else {
            // We're going to unpack this tarball into the global source
            // directory, but we want to make sure that it doesn't accidentally
            // (or maliciously) overwrite source code from other crates. Cargo
            // itself should never generate a tarball that hits this error, and
            // crates.io should also block uploads with these sorts of tarballs,
            // but be extra sure by adding a check here as well.
            anyhow::bail!(
                "invalid tarball downloaded, contains \
                     a file at {entry_path:?} which isn't under {prefix:?}",
            )
        }

        // Prevent unpacking symlinks and other unexpected entry types
        match entry.header().entry_type() {
            EntryType::Regular | EntryType::Directory => {}
            t => anyhow::bail!(
                "invalid tarball downloaded, contains an entry at {entry_path:?} with invalid type {t:?}",
            ),
        }

        // Prevent unpacking the lockfile from the crate itself.
        if entry_path
            .file_name()
            .map_or(false, |p| p == PACKAGE_SOURCE_LOCK)
        {
            continue;
        }
        // Unpacking failed
        bytes_written += entry.size();
        let mut result = entry.unpack_in(parent).map_err(anyhow::Error::from);
        if cfg!(windows) && restricted_names::is_windows_reserved_path(&entry_path) {
            result = result.with_context(|| {
                format!(
                    "`{}` appears to contain a reserved Windows path, \

View on GitHub (pinned to 98a09e7e7d)