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
- Re-package the crate with `cargo package` (which excludes symlinks), then re-publish.
- Replace the offending tarball in cache: `rm ~/.cargo/registry/cache/<index>/<pkg>-<ver>.crate` and refetch from a trusted registry.
- Remove symlinks from the crate's source tree before packaging.
- 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
- Strip symlinks from crate source trees before packaging.
- Always publish with `cargo package` / `cargo publish`.
- Audit registry mirrors for non-regular tar entries.
- Re-fetch suspect crates from crates.io before unpacking.
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
- invalid tarball downloaded, contains a file at
- failed to verify the checksum of
- failed to verify the checksum of
- path ` ` is not a blob in the git repo
- unable to read .cargo-ok file at
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)