Skip to content

feat: return error cells as a typed CellError instead of null#42

Merged
zanlucathiago merged 1 commit into
mainfrom
feat/error-cells
Jul 14, 2026
Merged

feat: return error cells as a typed CellError instead of null#42
zanlucathiago merged 1 commit into
mainfrom
feat/error-cells

Conversation

@zanlucathiago

Copy link
Copy Markdown
Owner

What's wrong

When a cell holds an Excel error — #DIV/0!, #N/A, #REF!, #VALUE!, #NAME?, #NUM!, #NULL! — the parser threw the error byte away and returned null. That made an errored cell indistinguishable from a blank one, which matters for the exact thing this library is for: importing/validating spreadsheet data.

The byte was right there — decodeBoolErr read it and discarded it, and a formula's cached error result did the same.

The change

Both paths now decode that byte into a typed value:

import { readFirstSheet, CellError } from "xls-reader";

for (const row of readFirstSheet(bytes)!.rows) {
  for (const cell of row) {
    if (cell instanceof CellError) console.warn(`errored: ${cell.code}`); // "#DIV/0!"
  }
}
  • CellError is distinct from null (blank) and from a text cell that literally contains "#N/A".
  • In JSON it serializes to { "code": "#DIV/0!" }, so the CLI output marks errors explicitly too.
  • The BIFF error-byte → code mapping lives in its own small module; an unknown byte falls back to null rather than guessing (keeps the hardening posture).
  • Exports the CellError class and ExcelErrorCode type; both READMEs updated.

Tests

  • errorFromByte: all seven defined codes, unknown-byte fallback, toString, and JSON shape.
  • decodeBoolErr: booleans, error → CellError, unknown → null.
  • decodeFormula: cached error result → CellError, cached blank → null.

80 tests pass; typecheck, lint, build, prettier all clean.

Version

Behavior change (error cells were null, now CellError), so it's a minor bump → 0.5.0. Callers who relied on error cells being null should treat a CellError as empty explicitly.

An error cell (#DIV/0!, #N/A, #REF!, ...) was collapsed to null, making it
indistinguishable from a blank cell — the error byte was read and thrown away in
decodeBoolErr and in a formula's cached error result. Both now decode it into a
new CellError value carrying the worksheet code, so importers can tell an errored
cell from an empty one, and from a text cell that literally contains "#N/A".

The BIFF error byte -> code mapping lives in its own small module; an unknown byte
falls back to null rather than guessing. CellError serializes to
{ "code": "#DIV/0!" } in JSON. Exports CellError and ExcelErrorCode, documented
in both READMEs.
@zanlucathiago zanlucathiago merged commit 8249bd0 into main Jul 14, 2026
3 checks passed
@zanlucathiago zanlucathiago deleted the feat/error-cells branch July 14, 2026 12:30
@github-actions github-actions Bot mentioned this pull request Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant