{"record":{"id":"ab21a4ff213be707","repo":"cube-js/cube","slug":"tar-skipped-an-entry-code-message","errorCode":null,"errorMessage":"tar skipped an entry (${code}): ${message}","messagePattern":"tar skipped an entry \\((.+?)\\): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/cubejs-templates/src/PackageFetcher.ts","lineNumber":70,"sourceCode":"    (await proxyFetch(url)).body.pipe(writer);\n\n    return new Promise((resolve, reject) => {\n      writer.on('finish', resolve as () => void);\n      writer.on('error', reject);\n    });\n  }\n\n  public async downloadPackages() {\n    await this.downloadRepo();\n\n    // Only ever a gzipped tar (GitHub's /archive/<ref>.tar.gz). `tar.x` refuses to\n    // write outside `cwd`: a leading `/` is stripped on extraction and entries containing `..` are\n    // dropped — but dropped with a warning rather than an error, so surface it.\n    await tar.x({\n      file: this.repoArchivePath,\n      cwd: this.tmpFolderPath,\n      preserveOwner: false,\n      onwarn: (code, message) => console.warn(`tar skipped an entry (${code}): ${message}`),\n    });\n\n    const dir = fs.readdirSync(this.tmpFolderPath).find((name) => !name.endsWith('tar.gz'));\n\n    if (!dir) {\n      throw new Error('No directory found');\n    }\n\n    fs.removeSync(path.resolve(this.tmpFolderPath, dir, 'yarn.lock'));\n    await executeCommand('npm', ['install'], { cwd: path.resolve(this.tmpFolderPath, dir) });\n\n    return {\n      packagesPath: path.join(this.tmpFolderPath, dir, 'packages'),\n    };\n  }\n\n  public cleanup() {\n    fs.removeSync(this.tmpFolderPath);","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-templates/src/PackageFetcher.ts#L52-L88","documentation":"During `PackageFetcher.downloadPackages()`, the GitHub repo archive (master.tar.gz) is extracted with node-tar's `tar.x`. Tar does not fail the extraction when an entry is unsafe (paths escaping cwd via `..` or absolute paths) — instead it emits a warning via `onwarn` with codes like TAR_BAD_ENTRIES / TAR_ENTRY_INVALID and silently skips the entry. Cube surfaces these warnings on console.warn because skipped entries can mean the extracted template tree is incomplete, which later manifests as a 'No directory found' error or missing packages.","triggerScenarios":"Extracting a downloaded repository tarball whose entries are unsafe: archive paths containing `..` segments, absolute paths, or otherwise refused entries that tar drops with a warning instead of an error. In practice this happens with a corrupted, tampered, or unexpectedly structured master.tar.gz downloaded from GitHub.","commonSituations":"Running `cubejs create` / template scaffolding in environments where a proxy or cache serves a modified/corrupted archive; a GitHub archive format change or symlink-heavy repo producing entries tar refuses; disk/permission issues in node_modules/.tmp causing entries to be skipped.","solutions":["Re-download a fresh archive: delete node_modules/.tmp (or run cleanup) so the stale/corrupt master.tar.gz is fetched again.","Check the console.warn output for the tar code and the skipped entry path to identify which entry is unsafe and why.","Verify the archive is intact: `tar -tzf node_modules/.tmp/master.tar.gz` and inspect any suspicious paths (leading `/`, `..`).","Ensure network path (proxy/VPN) is not intercepting/rewriting the GitHub archive download.","Upgrade @cubejs-backend/templates / node-tar to pick up fixes for entry handling, and retry the scaffolding command."],"exampleFix":"// before: warnings only, skipped entries silently lose files\nawait tar.x({ file: this.repoArchivePath, cwd: this.tmpFolderPath, preserveOwner: false,\n  onwarn: (code, message) => console.warn(`tar skipped an entry (${code}): ${message}`) });\n// after: fail hard on skipped entries so the fetcher never proceeds with a partial extraction\nawait tar.x({ file: this.repoArchivePath, cwd: this.tmpFolderPath, preserveOwner: false,\n  onwarn: (code, message) => { throw new Error(`tar skipped an entry (${code}): ${message}`); } });","handlingStrategy":"validation","validationCode":"import * as tar from 'tar';\nimport fs from 'fs';\n\nasync function validateArchiveSafe(archivePath: string, cwd: string) {\n  if (!fs.existsSync(archivePath) || fs.statSync(archivePath).size === 0) {\n    throw new Error(`Archive missing or empty: ${archivePath}`);\n  }\n  const unsafe: string[] = [];\n  await tar.t({ file: archivePath, onReadEntry: (e) => {\n    const p = e.path.replace(/^\\//, '');\n    if (p.includes('..') || e.path.startsWith('/')) unsafe.push(e.path);\n  }});\n  if (unsafe.length) throw new Error(`Unsafe tar entries: ${unsafe.join(', ')}`);\n}","typeGuard":"function isSafeTarPath(entryPath: string): boolean {\n  const normalized = entryPath.replace(/^\\//, '');\n  return !normalized.split('/').includes('..') && !entryPath.startsWith('/') && normalized.length > 0;\n}","tryCatchPattern":"try {\n  await fetcher.downloadPackages();\n} catch (err) {\n  if (err instanceof Error && /No directory found|tar skipped an entry/.test(err.message)) {\n    // corrupt/unsafe archive: clean and re-fetch\n    fetcher.cleanup();\n    await fetcher.downloadPackages();\n  } else { throw err; }\n}","preventionTips":["Monitor console.warn output during scaffolding — any 'tar skipped an entry' line means files were dropped.","Delete node_modules/.tmp before retrying so a stale or truncated master.tar.gz is not reused.","Verify downloaded archives with `tar -tzf` before extraction in CI or custom tooling.","Pin/upgrade node-tar and @cubejs-backend/templates versions to get fixed entry-handling behavior.","Avoid proxies that rewrite GitHub archive responses; download over a trusted network."],"tags":["tar","extraction","archive","filesystem","security"],"backgroundTag":"tar-entry-skipped","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}