{"record":{"id":"1a1d528d99796a60","repo":"n8n-io/n8n","slug":"tarball-not-found","errorCode":null,"errorMessage":"Tarball not found","messagePattern":"Tarball not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/scan-community-package/scanner/scanner.mjs","lineNumber":93,"sourceCode":"\t// Handle regular packages\n\tconst parts = packageSpec.split('@');\n\treturn { packageName: parts[0], version: parts[1] || null };\n};\n\nconst downloadAndExtractPackage = async (packageName, version) => {\n\ttry {\n\t\t// Download the tarball using safe arguments\n\t\tconst npmResult = spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`], {\n\t\t\tcwd: TEMP_DIR,\n\t\t\tstdio: 'pipe',\n\t\t\tshell: process.platform === 'win32',\n\t\t});\n\t\tif (npmResult.status !== 0) {\n\t\t\tthrow new Error(`npm pack failed: ${npmResult.stderr?.toString()}`);\n\t\t}\n\t\tconst tarballName = fs.readdirSync(TEMP_DIR).find((file) => file.endsWith('.tgz'));\n\t\tif (!tarballName) {\n\t\t\tthrow new Error('Tarball not found');\n\t\t}\n\n\t\t// Unpack the tarball\n\t\tconst packageDir = safeJoinPath(TEMP_DIR, `${packageName}-${version}`);\n\t\tfs.mkdirSync(packageDir, { recursive: true });\n\t\tconst tarResult = spawnSync(\n\t\t\t'tar',\n\t\t\t['-xzf', tarballName, '-C', packageDir, '--strip-components=1'],\n\t\t\t{\n\t\t\t\tcwd: TEMP_DIR,\n\t\t\t\tstdio: 'pipe',\n\t\t\t\tshell: process.platform === 'win32',\n\t\t\t},\n\t\t);\n\t\tif (tarResult.status !== 0) {\n\t\t\tthrow new Error(`tar extraction failed: ${tarResult.stderr?.toString()}`);\n\t\t}\n\t\tfs.unlinkSync(safeJoinPath(TEMP_DIR, tarballName));","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/scan-community-package/scanner/scanner.mjs#L75-L111","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure TEMP_DIR is a private, freshly created directory per scan invocation (mkdtemp) so no other process can consume the .tgz.","Pin the npm version used by the scanner so pack output location is stable.","Parse the tarball filename from npm pack's stdout (npm pack prints the filename) instead of globbing TEMP_DIR.","Disable concurrent scans against the same TEMP_DIR."],"exampleFix":"// before\nconst npmResult = spawnSync('npm', ['-q', 'pack', `${packageName}@${version}`], { cwd: TEMP_DIR });\nconst tarballName = fs.readdirSync(TEMP_DIR).find((f) => f.endsWith('.tgz'));\n\n// after - capture the filename npm pack prints\nconst npmResult = spawnSync('npm', ['pack', `${packageName}@${version}`], { cwd: TEMP_DIR, encoding: 'utf8' });\nconst tarballName = npmResult.stdout.trim().split(/\\r?\\n/).pop();\nif (!tarballName || !tarballName.endsWith('.tgz')) throw new Error('Tarball not found');","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nimport path from 'node:path';\n\nfunction ensureFreshTempDir(): string {\n  const dir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'n8n-scan-'));\n  return dir;\n}\n\n// use a private mkdtemp dir per scan so no other process can race for the .tgz","typeGuard":null,"tryCatchPattern":"const tarballName = fs.readdirSync(TEMP_DIR).find((f) => f.endsWith('.tgz'));\nif (!tarballName) {\n  const contents = fs.readdirSync(TEMP_DIR);\n  throw new Error(`Tarball not found after npm pack; TEMP_DIR contents: ${JSON.stringify(contents)}`);\n}","preventionTips":["Use mkdtempSync to give each scan a private TEMP_DIR - eliminates cross-scan races for the .tgz.","Parse the tarball filename from `npm pack`'s stdout instead of globbing the directory, so a stray file cannot confuse the scanner.","Disable concurrent scanner invocations against a shared TEMP_DIR.","If npm version changes, re-verify the pack output filename convention."],"tags":["npm","scanner","filesystem","concurrency"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}