{"record":{"id":"bf37ddaaee2f6a8e","repo":"denoland/deno","slug":"tar-entry-has-invalid-offset-size-offset","errorCode":null,"errorMessage":"tar entry '{}' has invalid offset/size (offset={}, size={})","messagePattern":"tar entry '(.+?)' has invalid offset/size \\(offset=(.+?), size=(.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/npm_cache/tarball_extract.rs","lineNumber":408,"sourceCode":"        .filter(|path| path.starts_with(output_folder))\n        .ok_or_else(|| {\n          ExtractTarballError::NotInOutputDirectory(path.to_path_buf())\n        })?\n    };\n    if !created_dirs.contains(dir_path) {\n      created_dirs.insert(dir_path.to_path_buf());\n      sys.fs_create_dir_all(dir_path)?;\n    }\n\n    let entry_type = entry.header().entry_type();\n    match entry_type {\n      EntryType::Regular => {\n        let open_options = OpenOptions::new_write();\n        let mut f = sys.fs_open(&absolute_path, &open_options)?;\n        let data_offset = entry.raw_file_position() as usize;\n        let size = entry.header().size()? as usize;\n        let end = data_offset.checked_add(size).ok_or_else(|| {\n          std::io::Error::new(\n            std::io::ErrorKind::InvalidData,\n            format!(\n              \"tar entry '{}' has invalid offset/size (offset={}, size={})\",\n              absolute_path.display(),\n              data_offset,\n              size,\n            ),\n          )\n        })?;\n        let entry_data =\n          tar_data.get(data_offset..end).ok_or_else(|| {\n            std::io::Error::new(\n              std::io::ErrorKind::UnexpectedEof,\n              format!(\n                \"tar entry '{}' extends beyond archive (offset={}, size={}, archive_len={})\",\n                absolute_path.display(),\n                data_offset,\n                size,","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/npm_cache/tarball_extract.rs#L390-L426","documentation":"During tarball extraction, each regular-file entry's raw data offset and header size are combined with checked_add; if offset + size overflows usize, the size field must be an absurd value (near 2^64), which no legitimate archive has. The extractor rejects it as InvalidData with the entry path, offset and size. This is a hard integrity check against corrupt or deliberately crafted tar headers.","triggerScenarios":"Extracting an npm package tarball whose header size field is huge (e.g. 0xFFFFFFFFFFFFFFFF), typically because the cached .tgz is corrupted, was truncated and re-assembled wrongly, or was tampered with by a proxy/MITM.","commonSituations":"A corrupted entry in $DENO_DIR npm cache after a crash or disk-full event; a corporate proxy mangling registry responses; a compromised or typosquatted package on a mirror registry; bit rot on the cache volume.","solutions":["Delete the cached tarball for that package/version under the npm cache directory and retry the install so it re-downloads.","Re-run with cache bypassed/reload (e.g. deno install with --reload) to force a fresh download.","If it reproduces with a fresh download, verify the tarball shasum against the registry metadata and report the package/mirror — a persistently bad header means the source is corrupt or malicious."],"exampleFix":"# before\ndeno install   # error: tar entry 'node_modules/foo/index.js' has invalid offset/size (offset=4096, size=18446744073709551615)\n\n# after\nrm -rf ~/.cache/deno/npm/registry.npmjs.org/foo\ndeno install --reload","handlingStrategy":"retry","validationCode":"// verify a tarball before extraction: header sizes must fit in the buffer\nfn tarball_headers_sane(tar_data: &[u8]) -> bool {\n  let mut ar = tar::Archive::new(tar_data);\n  ar.entries()\n    .map(|entries| {\n      entries\n        .filter_map(|e| e.ok())\n        .all(|e| e.raw_file_position() + e.header().size().unwrap_or(u64::MAX) as usize <= tar_data.len())\n    })\n    .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match extract_tarball(&tar_data, &dest) {\n  Ok(()) => {}\n  Err(err) if err.kind() == std::io::ErrorKind::InvalidData => {\n    // purge the corrupted cache entry and re-download once\n    fs::remove_dir_all(&cached_pkg_dir).ok();\n    let tar_data = client.download_again(&pkg).await?;\n    extract_tarball(&tar_data, &dest)?;\n  }\n  Err(err) => return Err(err),\n}","preventionTips":["Verify the tarball shasum against registry integrity metadata before extraction so corrupt/malicious artifacts are rejected earlier with a clearer error.","Write cache files atomically (temp file + rename) so a killed process cannot leave half-written tarballs.","Fetch npm packages over HTTPS from trusted registries only; pin mirrors you control."],"tags":["npm","tarball","archive","corruption","invalid-data","deno"],"backgroundTag":"corrupt-archive","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}