n8n-io/n8n · error

Failed to parse xlsx "${attachment.fileName}": ${message}

Error message

Failed to parse xlsx "${attachment.fileName}": ${message}

What it means

Thrown by extractXlsxAsRows (xlsx-parser.ts:34-37) when SheetJS XLSX.read throws while parsing the decoded buffer. The original error message is interpolated into the thrown Error so the underlying cause (corrupt zip, unsupported format, encryption) is visible. This is a parser-level failure distinct from the structural checks that follow.

Source

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

export async function extractXlsxAsRows(
	attachment: AttachmentInfo,
	attachmentIndex: number,
	input: ParseFileInput,
): Promise<ParseFileOutput> {
	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

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Re-save the workbook as a standard .xlsx from Excel/Google Sheets/LibreOffice.
  2. Remove password protection before attaching.
  3. Verify the file is a valid ZIP (e.g. check the PK header) before attaching.
  4. If the source is legacy .xls, convert to .xlsx or to CSV first.

Example fix

// before: attach legacy 'data.xls' renamed to 'data.xlsx'
// after:  open in Excel, 'Save As -> .xlsx', then attach
Defensive patterns

Strategy: try-catch

Validate before calling

// Cheap structural check before calling extractXlsxAsRows
const decoded = Buffer.from(attachment.data, 'base64');
const isZip = decoded.length >= 4 && decoded[0] === 0x50 && decoded[1] === 0x4b;
if (!isZip) throw new Error('Not a valid xlsx (ZIP) container');

Type guard

function looksLikeXlsxZip(buf: Buffer): boolean {
  return buf.length >= 4 && buf[0] === 0x50 && buf[1] === 0x4b && (buf[2] === 0x03 || buf[2] === 0x05 || buf[2] === 0x07);
}

Try / catch

try { return await extractXlsxAsRows(attachment, idx, input); }
catch (e) {
  if (e instanceof Error && e.message.startsWith('Failed to parse xlsx')) {
    // prompt user to re-save as standard .xlsx; do not retry the same bytes
  }
  throw e;
}

Prevention

When it happens

Trigger: A file labeled .xlsx that is not a valid OOXML zip: a legacy .xls saved with the wrong extension, a password-protected workbook, a truncated download, or a corrupted archive. Also triggered if SheetJS encounters an unsupported feature it cannot decode.

Common situations: User renames .xls to .xlsx; file was downloaded incompletely; workbook is encrypted; a third-party tool produced a non-conformant OOXML zip; version skew between SheetJS (@e965/xlsx) and the producing application.

Understand the failure class

Related errors


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