n8n-io/n8n · error · Error

Tarball not found

Error message

Tarball not found

What it means

Thrown by downloadAndExtractPackage when `npm pack` reported success (status 0) but no file ending in .tgz is present in TEMP_DIR. The scanner keys entirely off the .tgz extension, so any pack output that names the file differently (or writes elsewhere) breaks extraction before it starts.

Source

Thrown at packages/@n8n/scan-community-package/scanner/scanner.mjs:93

	// Handle regular packages
	const parts = packageSpec.split('@');
	return { packageName: parts[0], version: parts[1] || null };
};

const downloadAndExtractPackage = async (packageName, version) => {
	try {
		// Download the tarball using safe arguments
		const npmResult = spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`], {
			cwd: TEMP_DIR,
			stdio: 'pipe',
			shell: process.platform === 'win32',
		});
		if (npmResult.status !== 0) {
			throw new Error(`npm pack failed: ${npmResult.stderr?.toString()}`);
		}
		const tarballName = fs.readdirSync(TEMP_DIR).find((file) => file.endsWith('.tgz'));
		if (!tarballName) {
			throw new Error('Tarball not found');
		}

		// Unpack the tarball
		const packageDir = safeJoinPath(TEMP_DIR, `${packageName}-${version}`);
		fs.mkdirSync(packageDir, { recursive: true });
		const tarResult = spawnSync(
			'tar',
			['-xzf', tarballName, '-C', packageDir, '--strip-components=1'],
			{
				cwd: TEMP_DIR,
				stdio: 'pipe',
				shell: process.platform === 'win32',
			},
		);
		if (tarResult.status !== 0) {
			throw new Error(`tar extraction failed: ${tarResult.stderr?.toString()}`);
		}
		fs.unlinkSync(safeJoinPath(TEMP_DIR, tarballName));

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ensure TEMP_DIR is a private, freshly created directory per scan invocation (mkdtemp) so no other process can consume the .tgz.
  2. Pin the npm version used by the scanner so pack output location is stable.
  3. Parse the tarball filename from npm pack's stdout (npm pack prints the filename) instead of globbing TEMP_DIR.
  4. Disable concurrent scans against the same TEMP_DIR.

Example fix

// before
const npmResult = spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`], { cwd: TEMP_DIR });
const tarballName = fs.readdirSync(TEMP_DIR).find((f) => f.endsWith('.tgz'));

// after - capture the filename npm pack prints
const npmResult = spawnSync('npm', ['pack', `${packageName}@${version}`], { cwd: TEMP_DIR, encoding: 'utf8' });
const tarballName = npmResult.stdout.trim().split(/\r?\n/).pop();
if (!tarballName || !tarballName.endsWith('.tgz')) throw new Error('Tarball not found');
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
import path from 'node:path';

function ensureFreshTempDir(): string {
  const dir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'n8n-scan-'));
  return dir;
}

// use a private mkdtemp dir per scan so no other process can race for the .tgz

Try / catch

const tarballName = fs.readdirSync(TEMP_DIR).find((f) => f.endsWith('.tgz'));
if (!tarballName) {
  const contents = fs.readdirSync(TEMP_DIR);
  throw new Error(`Tarball not found after npm pack; TEMP_DIR contents: ${JSON.stringify(contents)}`);
}

Prevention

When it happens

Trigger: npm pack wrote the tarball to a different cwd than TEMP_DIR; an older/newer npm version prints to a different location; TEMP_DIR was cleared between the pack and the readdirSync; a leftover .tgz from a prior run was already consumed and npm wrote a file the scanner's filter misses.

Common situations: TEMP_DIR misconfigured or pointing at a node_modules symlink that resolves elsewhere; running scans concurrently against the same TEMP_DIR so two runs race for the .tgz; an npm version that emits '.tgz' with unexpected casing; an antivirus/quarantine removing the .tgz immediately after creation.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/1a1d528d99796a60. Report an issue: GitHub.