{"record":{"id":"6a7b0725bc38569b","repo":"n8n-io/n8n","slug":"xlsx-attachment-filename-has-no-sheets","errorCode":null,"errorMessage":"xlsx \"${attachment.fileName}\" has no sheets.","messagePattern":"xlsx \"(.+?)\" has no sheets\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/src/parsers/xlsx-parser.ts","lineNumber":41,"sourceCode":"\tconst decoded = Buffer.from(attachment.data, 'base64');\n\tif (decoded.length > MAX_DECODED_SIZE_BYTES) {\n\t\tthrow new Error(formatSizeLimitMessage(decoded.length));\n\t}\n\tassertOoxmlWithinBounds(decoded, attachment.fileName);\n\n\tconst XLSX = await import('@e965/xlsx');\n\n\tlet workbook: ReturnType<typeof XLSX.read>;\n\ttry {\n\t\tworkbook = XLSX.read(decoded, { type: 'buffer' });\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : 'unknown error';\n\t\tthrow new Error(`Failed to parse xlsx \"${attachment.fileName}\": ${message}`);\n\t}\n\n\tconst firstSheetName = workbook.SheetNames[0];\n\tif (!firstSheetName) {\n\t\tthrow new Error(`xlsx \"${attachment.fileName}\" has no sheets.`);\n\t}\n\n\tconst sheet = workbook.Sheets[firstSheetName];\n\tconst json = XLSX.utils.sheet_to_json<Record<string, unknown>>(sheet, {\n\t\tblankrows: false,\n\t\tdefval: null,\n\t});\n\n\tif (json.length === 0) {\n\t\tthrow new Error(`xlsx \"${attachment.fileName}\" sheet \"${firstSheetName}\" is empty.`);\n\t}\n\n\t// Round-trip through the JSON path of parseStructuredFile so types\n\t// (numbers, booleans) survive and we share row/column budget logic.\n\tconst jsonAttachment: AttachmentInfo = {\n\t\tdata: Buffer.from(JSON.stringify(json), 'utf-8').toString('base64'),\n\t\tmimeType: 'application/json',\n\t\tfileName: attachment.fileName,","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/src/parsers/xlsx-parser.ts#L23-L59","documentation":"Thrown by extractXlsxAsRows (xlsx-parser.ts:39-42) when workbook.SheetNames is empty or its first entry is falsy. SheetJS parsed the zip successfully but found no sheets at all, so there is nothing to extract. This is a content-level rejection that happens after the parse try/catch and before sheet_to_json.","triggerScenarios":"A valid OOXML zip with an empty workbook part; a workbook whose sheets were all deleted; a workbook where SheetNames is present but empty. The XLSX.read call itself succeeded.","commonSituations":"User deleted all sheets before saving; a template/export pipeline emitted a workbook stub with no sheets; a programmatic generator wrote the workbook structure but no sheet parts.","solutions":["Open the workbook and confirm at least one sheet exists; re-save.","Regenerate the file from the source pipeline with the intended sheet.","If the file is a template, fill it before attaching.","Attach a different, non-empty file."],"exampleFix":"// before: attach 'empty.xlsx' with 0 sheets\n// after:  add at least one sheet with data, re-save, attach","handlingStrategy":"try-catch","validationCode":"// After parsing the workbook yourself (or pre-check):\n// import * as XLSX from '@e965/xlsx';\n// const wb = XLSX.read(decoded, { type: 'buffer' });\n// if (!wb.SheetNames.length) throw new Error('workbook has no sheets');","typeGuard":"function workbookHasSheets(wb: { SheetNames: string[] }): boolean {\n  return Array.isArray(wb.SheetNames) && wb.SheetNames.length > 0;\n}","tryCatchPattern":"try { return await extractXlsxAsRows(attachment, idx, input); }\ncatch (e) {\n  if (e instanceof Error && /has no sheets$/.test(e.message)) {\n    // ask for a workbook with at least one sheet\n  }\n  throw e;\n}","preventionTips":["Confirm the source workbook has data before exporting.","Avoid attaching template stubs with no sheets.","Validate with SheetJS in a pre-check if untrusted."],"tags":["xlsx","empty-file","attachment-parsing","validation"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}