cube-js/cube · warning

tar skipped an entry (${code}): ${message}

Error message

tar skipped an entry (${code}): ${message}

What it means

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.

Source

Thrown at packages/cubejs-templates/src/PackageFetcher.ts:70

    (await proxyFetch(url)).body.pipe(writer);

    return new Promise((resolve, reject) => {
      writer.on('finish', resolve as () => void);
      writer.on('error', reject);
    });
  }

  public async downloadPackages() {
    await this.downloadRepo();

    // Only ever a gzipped tar (GitHub's /archive/<ref>.tar.gz). `tar.x` refuses to
    // write outside `cwd`: a leading `/` is stripped on extraction and entries containing `..` are
    // dropped — but dropped with a warning rather than an error, so surface it.
    await tar.x({
      file: this.repoArchivePath,
      cwd: this.tmpFolderPath,
      preserveOwner: false,
      onwarn: (code, message) => console.warn(`tar skipped an entry (${code}): ${message}`),
    });

    const dir = fs.readdirSync(this.tmpFolderPath).find((name) => !name.endsWith('tar.gz'));

    if (!dir) {
      throw new Error('No directory found');
    }

    fs.removeSync(path.resolve(this.tmpFolderPath, dir, 'yarn.lock'));
    await executeCommand('npm', ['install'], { cwd: path.resolve(this.tmpFolderPath, dir) });

    return {
      packagesPath: path.join(this.tmpFolderPath, dir, 'packages'),
    };
  }

  public cleanup() {
    fs.removeSync(this.tmpFolderPath);

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Re-download a fresh archive: delete node_modules/.tmp (or run cleanup) so the stale/corrupt master.tar.gz is fetched again.
  2. Check the console.warn output for the tar code and the skipped entry path to identify which entry is unsafe and why.
  3. Verify the archive is intact: `tar -tzf node_modules/.tmp/master.tar.gz` and inspect any suspicious paths (leading `/`, `..`).
  4. Ensure network path (proxy/VPN) is not intercepting/rewriting the GitHub archive download.
  5. Upgrade @cubejs-backend/templates / node-tar to pick up fixes for entry handling, and retry the scaffolding command.

Example fix

// before: warnings only, skipped entries silently lose files
await tar.x({ file: this.repoArchivePath, cwd: this.tmpFolderPath, preserveOwner: false,
  onwarn: (code, message) => console.warn(`tar skipped an entry (${code}): ${message}`) });
// after: fail hard on skipped entries so the fetcher never proceeds with a partial extraction
await tar.x({ file: this.repoArchivePath, cwd: this.tmpFolderPath, preserveOwner: false,
  onwarn: (code, message) => { throw new Error(`tar skipped an entry (${code}): ${message}`); } });
Defensive patterns

Strategy: validation

Validate before calling

import * as tar from 'tar';
import fs from 'fs';

async function validateArchiveSafe(archivePath: string, cwd: string) {
  if (!fs.existsSync(archivePath) || fs.statSync(archivePath).size === 0) {
    throw new Error(`Archive missing or empty: ${archivePath}`);
  }
  const unsafe: string[] = [];
  await tar.t({ file: archivePath, onReadEntry: (e) => {
    const p = e.path.replace(/^\//, '');
    if (p.includes('..') || e.path.startsWith('/')) unsafe.push(e.path);
  }});
  if (unsafe.length) throw new Error(`Unsafe tar entries: ${unsafe.join(', ')}`);
}

Type guard

function isSafeTarPath(entryPath: string): boolean {
  const normalized = entryPath.replace(/^\//, '');
  return !normalized.split('/').includes('..') && !entryPath.startsWith('/') && normalized.length > 0;
}

Try / catch

try {
  await fetcher.downloadPackages();
} catch (err) {
  if (err instanceof Error && /No directory found|tar skipped an entry/.test(err.message)) {
    // corrupt/unsafe archive: clean and re-fetch
    fetcher.cleanup();
    await fetcher.downloadPackages();
  } else { throw err; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/ab21a4ff213be707. Report an issue: GitHub.