iflytek/astron-agent · error · IllegalArgumentException

Header mismatch! Expected headers: , Actual headers:

Error message

Header mismatch! Expected headers: , Actual headers: 

What it means

DBExcelReadListener.invokeHeadMap (an EasyExcel listener callback) compares the Excel header row against the required fields of the target DB table (DbTableField entries with isRequired=true). If the collection of expected headers is not an equal collection (same elements, any order) to the actual headers, it throws IllegalArgumentException 'Header mismatch! Expected headers: [...], Actual headers: [...]'. The imported spreadsheet's columns must cover exactly the table's required fields.

Solutions

  1. Compare the Expected/Actual lists in the exception message and fix the spreadsheet so its header row exactly matches the required table fields.
  2. Re-download/regenerate the import template from the current table schema instead of reusing an old file.
  3. Normalize headers (trim whitespace, unify case) before comparison in invokeHeadMap to tolerate cosmetic differences.
  4. If a new required column was added to the table, update all distributed templates and re-import.
  5. Catch IllegalArgumentException at the import entry point and return a user-friendly message listing the mismatched headers.

Example fix

// before
List<String> expectedHeaders = fields.stream()... .collect(Collectors.toList()); // raw, case/space sensitive

// after: normalize before comparing
List<String> expectedHeaders = fields.stream()
        .filter(DbTableField::getIsRequired)
        .map(f -> f.getName().trim().toLowerCase())
        .sorted()
        .collect(Collectors.toList());
List<String> normalizedActual = actualHeaders.stream()
        .map(h -> h == null ? "" : h.trim().toLowerCase())
        .sorted()
        .collect(Collectors.toList());
if (!expectedHeaders.equals(normalizedActual)) { throw ... }
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the template's header row before invoking EasyExcel read
List<String> expected = requiredFields.stream().map(f -> f.getName().trim().toLowerCase()).sorted().collect(Collectors.toList());
List<String> actual = readHeaderRow(file).stream().map(h -> h == null ? "" : h.trim().toLowerCase()).sorted().collect(Collectors.toList());
if (!expected.equals(actual)) {
    throw new IllegalArgumentException("Template headers do not match required table fields. Download a fresh template.");
}

Try / catch

try {
    EasyExcel.read(file.getInputStream(), DBExcelReadListener.class / listener).sheet().doRead();
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Header mismatch!")) {
        // show expected vs actual headers to the user; prompt to re-download the template
        return errorResponse(e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: Importing an Excel/CSV file whose header row does not exactly match the set of required DB table fields: a required column missing, an extra/unexpected column, renamed headers, extra whitespace/case differences, or duplicate column names changing the multiset equality.

Common situations: Users hand-editing exported templates (deleting or renaming columns); table schema changed (new required field) after the template was distributed; template exported from an older schema version; locale/encoding mangling header text; trailing spaces in header cells.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/aa49509c743ae86c. Report an issue: GitHub.

Appendix: source

Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/database/DBExcelReadListener.java:67

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        List<String> actualHeaders = new ArrayList<>(headMap.values());

        expectedHeaders = tableFields.stream()
                .map(DbTableField::getName)
                .filter(n -> !Arrays.asList(SYSTEM_FIELDS).contains(n))
                .collect(Collectors.toList());

        notNullFieldsList = tableFields.stream()
                .filter(f -> !Arrays.asList(SYSTEM_FIELDS).contains(f.getName()))
                .filter(DbTableField::getIsRequired)
                .map(DbTableField::getName)
                .collect(Collectors.toList());

        // Here requires consistent order: maintain consistency with your original logic
        if (!CollectionUtils.isEqualCollection(expectedHeaders, actualHeaders)) {
            throw new IllegalArgumentException("Header mismatch! Expected headers: " + expectedHeaders + ", Actual headers: " + actualHeaders);
        } else {
            expectedHeaders = actualHeaders;
        }
        headerValidated = true;
    }

    @Override
    public void invoke(Map<Integer, String> row, AnalysisContext context) {
        if (!headerValidated) {
            throw new BusinessException(ResponseEnum.RESPONSE_FAILED, "Headers not yet validated, please check Excel file.");
        }
        if (accepted >= maxRows) {
            return; // Exceed limit, directly ignore subsequent rows to ensure availability
        }

        Map<String, Object> out = new LinkedHashMap<>();
        out.put("uid", uid);

View on GitHub (pinned to 5e758547a8)