{"record":{"id":"0c680fb811022d49","repo":"n8n-io/n8n","slug":"invalid-file-type","errorCode":null,"errorMessage":"Invalid file type","messagePattern":"Invalid file type","errorType":"exception","errorClass":"Error","httpStatus":500,"severity":"error","filePath":"packages/cli/src/controllers/e2e.controller.ts","lineNumber":417,"sourceCode":"\t\t\tsizeBytes: stats.size,\n\t\t\tsizeMB: Math.round(stats.size / 1024 / 1024),\n\t\t};\n\t}\n\n\tprivate heapSnapshotPaths = new Map<string, string>();\n\n\t/**\n\t * Download a heap snapshot file as a stream.\n\t * The response-helper pipes Readable streams directly to the response.\n\t */\n\t@Get('/heap-snapshot/:filename', { skipAuth: true })\n\tdownloadHeapSnapshot(req: Request) {\n\t\tconst fs = require('node:fs') as typeof nodeFs;\n\t\tconst path = require('node:path') as typeof nodePath;\n\n\t\tconst filename = path.basename(req.params.filename);\n\t\tif (!filename.endsWith('.heapsnapshot')) {\n\t\t\tthrow new Error('Invalid file type');\n\t\t}\n\n\t\t// Look up the full path stored during POST, or try cwd\n\t\tconst filePath = this.heapSnapshotPaths.get(filename) ?? path.resolve(filename);\n\t\tif (!fs.existsSync(filePath)) {\n\t\t\tthrow new Error(`Snapshot not found: ${filename} (tried ${filePath})`);\n\t\t}\n\n\t\treturn fs.createReadStream(filePath);\n\t}\n\n\t// --- Per-spec backend V8 coverage (DEVP-370) -----------------------------\n\t// Lets the Playwright coverage fixture attribute this main process's V8\n\t// coverage to the spec that produced it, so backend source files land in the\n\t// E2E impact map (today the map is frontend-only). Test-only: the whole\n\t// controller hard-exits outside E2E (see top of file).\n\t//\n\t// Uses `Profiler.getBestEffortCoverage` (NON-draining) + a server-side","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/e2e.controller.ts#L399-L435","documentation":"Plain `Error('Invalid file type')` thrown at e2e.controller.ts:417 by the heap-snapshot download route when `req.params.filename` (after `path.basename`) does not end with `.heapsnapshot`. This controller is test-only — it hard-exits the process unless running under E2E (see top of file). The thrown `Error` is not a typed n8n error and will be serialized to a 500 by the generic response handler.","triggerScenarios":"In E2E/test mode only: hitting `GET /e2e/heap-snapshot/<filename>` with a filename whose extension isn't `.heapsnapshot` — e.g. `.json`, `.txt`, or no extension. Cannot occur in production because the controller aborts startup outside E2E.","commonSituations":"Playwright fixtures building the wrong URL; manual curl during local E2E runs; typos in fixture file paths.","solutions":["Pass a filename ending in `.heapsnapshot` produced by the POST heap-snapshot route.","Use the filename exactly as stored in `heapSnapshotPaths` (set during POST).","If writing a test fixture, assert the extension before constructing the URL."],"exampleFix":"// before\nGET /e2e/heap-snapshot/dump.json\n// 500 Invalid file type\n\n// after\nGET /e2e/heap-snapshot/dump.heapsnapshot","handlingStrategy":"validation","validationCode":"function assertHeapFile(name: string) {\n  if (!name.endsWith('.heapsnapshot')) throw new TypeError('Invalid file type');\n}","typeGuard":"const isHeapSnapshotFile = (f: unknown): f is string =>\n  typeof f === 'string' && f.endsWith('.heapsnapshot');","tryCatchPattern":null,"preventionTips":["Use only filenames returned by the POST /e2e/heap-snapshot route.","Assert the `.heapsnapshot` extension in test fixtures before constructing URLs.","Remember this controller exists only in E2E mode — it never runs in production."],"tags":["e2e","test-only","heap-snapshot","bad-request"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}