{"record":{"id":"c3bd0757e2e92757","repo":"denoland/deno","slug":"invalid-extract-destination","errorCode":null,"errorMessage":"Invalid extract destination: {}","messagePattern":"Invalid extract destination: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"cli/tools/installer/npm_compat.rs","lineNumber":1057,"sourceCode":"      continue;\n    }\n    let _ = std::fs::remove_dir_all(path);\n  }\n}\n\n/// Extract a gzipped npm-style tarball so that `dest` is never observable in a\n/// half-extracted state: unpack into a sibling staging directory on the same\n/// filesystem, then `rename` it into place.\n///\n/// Concurrent `deno check` invocations sharing a cache race to materialize the\n/// same package. The rename makes each one either publish a complete tree or\n/// lose harmlessly to a winner that already did.\nfn extract_tarball_gz_atomic(\n  gz_bytes: &[u8],\n  dest: &Path,\n) -> Result<(), AnyError> {\n  let parent = dest.parent().ok_or_else(|| {\n    anyhow!(\"Invalid extract destination: {}\", dest.display())\n  })?;\n  std::fs::create_dir_all(parent)?;\n\n  // Stage as a sibling so the rename stays within one filesystem. The pid plus\n  // a process-local counter keeps concurrent extractions - across processes and\n  // within one - from sharing a staging dir.\n  static STAGING_COUNTER: std::sync::atomic::AtomicU64 =\n    std::sync::atomic::AtomicU64::new(0);\n  let name = dest.file_name().and_then(|n| n.to_str()).ok_or_else(|| {\n    anyhow!(\"Invalid extract destination: {}\", dest.display())\n  })?;\n  // The leading dot is required because this parent is passed to TypeScript as\n  // a typeRoots directory, and TypeScript ignores dot-prefixed entries when it\n  // enumerates type packages.\n  let staging_prefix = format!(\".{name}.tmp-\");\n  // Reclaim staging dirs left by killed processes. Keep recent dirs because\n  // they may belong to another extraction currently racing with this one.\n  if let Some(stale_before) = std::time::SystemTime::now()","sourceCodeStart":1039,"sourceCodeEnd":1075,"githubUrl":"https://github.com/denoland/deno/blob/336da420f4343cbb1dcbd5eed9d075ff555ed6ee/cli/tools/installer/npm_compat.rs#L1039-L1075","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet dest = Path::new(\"react\");\nextract_tarball_gz_atomic(&gz, dest)?;\n// after\nlet dest = std::path::absolute(\"react\")?; // ensures a parent exists\nextract_tarball_gz_atomic(&gz, &dest)?;","handlingStrategy":"validation","validationCode":"if (dest.parent().is_none()) {\n  throw new Error(`extract destination needs a directory component: ${dest.display()}`);\n}","typeGuard":"fn has_parent(p: &Path) -> bool { p.parent().is_some() }","tryCatchPattern":null,"preventionTips":["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."],"tags":["filesystem","path","npm","installation"],"backgroundTag":"invalid-argument-value","analyzedSha":"336da420f4343cbb1dcbd5eed9d075ff555ed6ee","analyzedAt":"2026-09-11T17:12:50.272Z","contentChangedAt":"2026-09-11T17:12:50.272Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}