n8n-io/n8n · error

xlsx "${attachment.fileName}" has no sheets.

Error message

xlsx "${attachment.fileName}" has no sheets.

What it means

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.

Source

Thrown at packages/@n8n/instance-ai/src/parsers/xlsx-parser.ts:41

	const decoded = Buffer.from(attachment.data, 'base64');
	if (decoded.length > MAX_DECODED_SIZE_BYTES) {
		throw new Error(formatSizeLimitMessage(decoded.length));
	}
	assertOoxmlWithinBounds(decoded, attachment.fileName);

	const XLSX = await import('@e965/xlsx');

	let workbook: ReturnType<typeof XLSX.read>;
	try {
		workbook = XLSX.read(decoded, { type: 'buffer' });
	} catch (error) {
		const message = error instanceof Error ? error.message : 'unknown error';
		throw new Error(`Failed to parse xlsx "${attachment.fileName}": ${message}`);
	}

	const firstSheetName = workbook.SheetNames[0];
	if (!firstSheetName) {
		throw new Error(`xlsx "${attachment.fileName}" has no sheets.`);
	}

	const sheet = workbook.Sheets[firstSheetName];
	const json = XLSX.utils.sheet_to_json<Record<string, unknown>>(sheet, {
		blankrows: false,
		defval: null,
	});

	if (json.length === 0) {
		throw new Error(`xlsx "${attachment.fileName}" sheet "${firstSheetName}" is empty.`);
	}

	// Round-trip through the JSON path of parseStructuredFile so types
	// (numbers, booleans) survive and we share row/column budget logic.
	const jsonAttachment: AttachmentInfo = {
		data: Buffer.from(JSON.stringify(json), 'utf-8').toString('base64'),
		mimeType: 'application/json',
		fileName: attachment.fileName,

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Open the workbook and confirm at least one sheet exists; re-save.
  2. Regenerate the file from the source pipeline with the intended sheet.
  3. If the file is a template, fill it before attaching.
  4. Attach a different, non-empty file.

Example fix

// before: attach 'empty.xlsx' with 0 sheets
// after:  add at least one sheet with data, re-save, attach
Defensive patterns

Strategy: try-catch

Validate before calling

// After parsing the workbook yourself (or pre-check):
// import * as XLSX from '@e965/xlsx';
// const wb = XLSX.read(decoded, { type: 'buffer' });
// if (!wb.SheetNames.length) throw new Error('workbook has no sheets');

Type guard

function workbookHasSheets(wb: { SheetNames: string[] }): boolean {
  return Array.isArray(wb.SheetNames) && wb.SheetNames.length > 0;
}

Try / catch

try { return await extractXlsxAsRows(attachment, idx, input); }
catch (e) {
  if (e instanceof Error && /has no sheets$/.test(e.message)) {
    // ask for a workbook with at least one sheet
  }
  throw e;
}

Prevention

When it happens

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

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

Related errors


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