Skip to content

[Bug] XLS literal error cells (#DIV/0!, #N/A, #NULL!) are read as Boolean true/false #1010

Description

@codeAnqiang-ma

Search before asking

  • I searched in the issues and found nothing similar.

Fesod version

main @ c8224b8 (2.1.0-SNAPSHOT)

JDK version

OpenJDK 21.0.5 (Temurin)

Operating system

macOS 15

Steps To Reproduce

A cell can hold a literal error value — an error stored as the cell value itself rather than as a formula result. Excel produces these with "Paste Special → Values" over a formula error, and by typing #N/A directly into a cell.

Write the same row to both formats with POI and read both back with Fesod:

// same content written to both .xls (HSSFWorkbook) and .xlsx (XSSFWorkbook)
Row row = workbook.createSheet("sheet").createRow(0);
row.createCell(0).setCellValue(true);
row.createCell(1).setCellErrorValue(FormulaError.DIV0.getCode());
row.createCell(2).setCellErrorValue(FormulaError.NA.getCode());
row.createCell(3).setCellErrorValue(FormulaError.NULL.getCode());
row.createCell(4).setCellValue("marker");

List<Map<Integer, Object>> rows =
        FesodSheet.read(file).headRowNumber(0).sheet(0).doReadSync();

Current Behavior

xls  row0 = {0=true, 1=true, 2=true, 3=false, 4=marker}
xlsx row0 = {0=true, 1=#DIV/0!, 2=#N/A, 3=#NULL!, 4=marker}

The three error cells come back from .xls as booleans. Note that they do not even agree with each other: #DIV/0! and #N/A become true while #NULL! becomes false.

Apache POI's own usermodel reads all three as CellType.ERROR from both files, so the data is written correctly and only the read path differs.

The result is a silent data error: an error marker becomes a plausible-looking boolean, both the "this is an error" fact and the specific error type are lost, and the same workbook content yields different values depending on whether it was saved as .xls or .xlsx.

Expected Behavior

.xls should return the error text, the same as .xlsx already does:

xls  row0 = {0=true, 1=#DIV/0!, 2=#N/A, 3=#NULL!, 4=marker}

Anything else?

Root cause. BoolErrRecordHandler#processRecord calls ber.getBooleanValue() unconditionally:

https://github.com/apache/fesod/blob/c8224b8/fesod-sheet/src/main/java/org/apache/fesod/sheet/analysis/v03/handlers/BoolErrRecordHandler.java#L55-L59

A BIFF BOOLERR record stores either a boolean or an error code, distinguished by its fError flag (POI exposes this as isBoolean() / isError()). For an error record POI's getBooleanValue() returns errorCode != 0, which is why #DIV/0! (code 7) and #N/A (code 42) turn into true and #NULL! (code 0) turns into false.

The handler therefore only implements the boolean half of the record type. This is inconsistent with three places in Fesod itself:

  • the XLSX read path — CellTagHandler maps t="e" cells to the error text;
  • the XLS formula read path — FormulaRecordHandler maps a formula whose cached result is an error to CellDataTypeEnum.ERROR;
  • StringErrorConverter, which exists specifically to convert ERROR cells to their text.

I'm happy to open a PR: branch on isError() and produce an ERROR cell carrying the error text, so the existing StringErrorConverter path yields the same user-visible value as XLSX.

This report was produced with AI assistance; I reproduced the behaviour locally and reviewed every conclusion myself.

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions