nocobase/nocobase · error · ImportValidationError
Headers not found. Expected headers: {{headers}}
Error message
Headers not found. Expected headers: {{headers}} What it means
ImportValidationError thrown by getData when the uploaded spreadsheet contains no header row matching the expected headers derived from the import columns. findAndValidateHeaders scans the first rows for a row containing the expected header titles; headerRowIndex === -1 means none matched, and the error lists the expected headers so users can align their file.
Source
Thrown at packages/plugins/@nocobase/plugin-action-import/src/server/services/xlsx-importer.ts:652
const columns = this.getColumnsByPermission(ctx);
return columns.map((col) => col.title || col.defaultTitle);
}
async getData(ctx?: Context) {
const workbook = this.options.workbook;
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
let data = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: null, blankrows: false }) as string[][];
// Release the workbook reference immediately after converting to plain data array,
// so the large parsed XLSX object can be garbage collected during import.
this.options.workbook = null;
// Find and validate header row
const expectedHeaders = this.getExpectedHeaders(ctx);
const { headerRowIndex, headers } = this.findAndValidateHeaders({ data, expectedHeaders });
if (headerRowIndex === -1) {
throw new ImportValidationError('Headers not found. Expected headers: {{headers}}', {
headers: expectedHeaders.join(', '),
});
}
data = this.alignWithHeaders({ data, expectedHeaders, headerRowIndex });
// Extract data rows
const rows = data.slice(headerRowIndex + 1);
// if no data rows, throw error
if (rows.length === 0) {
throw new ImportValidationError('No data to import');
}
return [headers, ...rows];
}
private alignWithHeaders(params: {
data: string[][];
expectedHeaders: string[];View on GitHub (pinned to fa42722fef)
Solutions
- Re-download the import template and fill it in without editing header row titles.
- Ensure the sheet's header row exactly contains the expected headers listed in the error message.
- Remove decorative title rows/merged cells above the real header row, or start data at the template's structure.
- In code, verify getExpectedHeaders(ctx) titles match the actual sheet before calling getData.
Example fix
// before (Excel row 1) "User Name | Email Address" // after "Username | Email" // exactly matching expected headers: "Username, Email"
Defensive patterns
Strategy: validation
Validate before calling
const expected = importer.getExpectedHeaders(ctx); // or rebuild from columns
const firstRows = data.slice(0, 10).map((r) => r.map((c) => String(c ?? '').trim()));
const matched = firstRows.some((r) => expected.every((h) => r.includes(h)));
if (!matched) throw new Error(`Missing headers; expected: ${expected.join(', ')}`); Try / catch
try {
await importer.data();
} catch (e) {
if (e.name === 'ImportValidationError' && e.message.includes('Headers not found')) {
console.error('Expected headers:', e.context?.headers);
}
throw e;
} Prevention
- Fill the downloaded template without renaming header cells.
- Remove decorative rows/merged cells above the header row.
- Upload the correct sheet/file; check active sheet in Excel.
- Keep column titles stable across locales for shared templates.
When it happens
Trigger: Excel file columns renamed, reordered with different titles, or the user uploads an empty/other sheet; extra title rows or the template's first rows changed; columns configured with custom titles that don't appear in the file.
Common situations: Downloading a template, editing headers in Excel, then importing; uploading the wrong file; localized/translated column titles differing between template generation and header matching; merged/blank top rows pushing headers beyond the scan range.
Related errors
- Failed to parse field {{field}} in row {{rowIndex}}: {{messa
- columns is empty
- Columns configuration is empty
- Invalid field: {{field}}
- Field not found: {{field}}
AI-assisted analysis of nocobase/nocobase@fa42722fef (2026-09-01).
Data as JSON: /api/errors/432107a1e9ae6574.
Report an issue: GitHub.