denoland/deno · error · anyhow::Error
Invalid extract destination: {}
Error message
Invalid extract destination: {} What it means
extract_tarball_gz_atomic extracts an npm tarball into an atomic staging directory placed as a sibling of the destination. Before staging, it needs the destination's parent directory via Path::parent(); if the path has no parent (e.g. a bare relative name like "foo" with no directory component, or the filesystem root), this anyhow error is thrown because the atomic-rename strategy cannot proceed.
Source
Thrown at cli/tools/installer/npm_compat.rs:1057
continue;
}
let _ = std::fs::remove_dir_all(path);
}
}
/// Extract a gzipped npm-style tarball so that `dest` is never observable in a
/// half-extracted state: unpack into a sibling staging directory on the same
/// filesystem, then `rename` it into place.
///
/// Concurrent `deno check` invocations sharing a cache race to materialize the
/// same package. The rename makes each one either publish a complete tree or
/// lose harmlessly to a winner that already did.
fn extract_tarball_gz_atomic(
gz_bytes: &[u8],
dest: &Path,
) -> Result<(), AnyError> {
let parent = dest.parent().ok_or_else(|| {
anyhow!("Invalid extract destination: {}", dest.display())
})?;
std::fs::create_dir_all(parent)?;
// Stage as a sibling so the rename stays within one filesystem. The pid plus
// a process-local counter keeps concurrent extractions - across processes and
// within one - from sharing a staging dir.
static STAGING_COUNTER: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(0);
let name = dest.file_name().and_then(|n| n.to_str()).ok_or_else(|| {
anyhow!("Invalid extract destination: {}", dest.display())
})?;
// The leading dot is required because this parent is passed to TypeScript as
// a typeRoots directory, and TypeScript ignores dot-prefixed entries when it
// enumerates type packages.
let staging_prefix = format!(".{name}.tmp-");
// Reclaim staging dirs left by killed processes. Keep recent dirs because
// they may belong to another extraction currently racing with this one.
if let Some(stale_before) = std::time::SystemTime::now()View on GitHub (pinned to 336da420f4)
Solutions
- Pass a full destination path that includes a directory component, e.g. use an absolute path under a cache dir.
- Canonicalize or make the destination absolute (std::path::absolute / cwd.join) before calling.
- Check dest.parent().is_some() before invoking the install/extract API.
- Verify the DENO_DIR / npm cache configuration points to a real subdirectory, not a root.
Example fix
// before
let dest = Path::new("react");
extract_tarball_gz_atomic(&gz, dest)?;
// after
let dest = std::path::absolute("react")?; // ensures a parent exists
extract_tarball_gz_atomic(&gz, &dest)?; Defensive patterns
Strategy: validation
Validate before calling
if (dest.parent().is_none()) {
throw new Error(`extract destination needs a directory component: ${dest.display()}`);
} Type guard
fn has_parent(p: &Path) -> bool { p.parent().is_some() } Prevention
- Always build destinations with a directory prefix (cache_dir.join(name)).
- Make paths absolute before installation APIs.
- Never pass bare filenames or root paths as extraction destinations.
When it happens
Trigger: Calling download_npm_package or install_jsr_packages with a dest path that has no parent component — e.g. dest == "pkg" (relative, no slash), dest == "/", or an empty/normalized-away path. Path::parent() then returns None.
Common situations: Programmatically built paths where a join() collapsed to a bare filename; passing the current directory's files directly instead of a directory path; running from a context that strips the directory prefix; misconfigured DENO_DIR pointing at a root-level path.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ERR_INVALID_ARG_TYPE
- the source path is not a valid file
- refusing to materialize package into symlinked directory
- LAUFEY cache dir has no parent: {}
- LAUFEY backend binary has no parent directory: {}
AI-assisted analysis of denoland/deno@336da420f4 (2026-09-11).
Data as JSON: /api/errors/c3bd0757e2e92757.
Report an issue: GitHub.