Skip to content

fix(xlsx): preserve saved Excel error values - #2766

Open
Deepika Mundla (dpka2206) wants to merge 1 commit into
microsoft:mainfrom
dpka2206:fix/xlsx-preserve-excel-error-values
Open

Deepika Mundla (dpka2206) wants to merge 1 commit into
microsoft:mainfrom
dpka2206:fix/xlsx-preserve-excel-error-values

Conversation

@dpka2206

@dpka2206 Deepika Mundla (dpka2206) commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

  • Excel error cells such as #DIV/0! and #REF! were converted to NaN because pandas drops data_type == "e" values inside its openpyxl reader, before Markdown is built.
  • After pandas formats the sheet, write each saved error token back into its HTML cell. A normal value still renders as it does today, and a blank cell still renders as NaN.
  • Fixes XLSX conversion loses Excel error values such as #DIV/0! and #REF! #2728.

Test plan

  • pytest packages/markitdown/tests/test_xlsx.py (26 passed)
  • The issue workbook keeps #DIV/0!, #REF!, #VALUE!, #N/A, #NUM!, #NAME?, and #NULL!, keeps 42.0, and keeps the blank cell as NaN

@dpka2206

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Pandas maps Excel error cells to NaN before Markdown is rendered, so a
#DIV/0! cell looks the same as a blank. Restore the saved error token
after the sheet is formatted.
@dpka2206
Deepika Mundla (dpka2206) force-pushed the fix/xlsx-preserve-excel-error-values branch from e1496b2 to 4323e63 Compare October 25, 2026 06:55

@rahul05ranjan Rahul (rahul05ranjan) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified the core approach independently with pandas 2.2.3/openpyxl 3.1.5: an error cell like #DIV/0! does surface as NaN in the DataFrame (so the post-render patch is the only place it can be recovered), and the rows[cell.row - 1][cell.column - 1] indexing held up in the shapes I tried — including ones your test doesn't cover:

  • a leading fully-blank row above the header (pandas keeps it as an Unnamed header and the tr/row mapping stays 1:1),
  • a fully-blank row in the middle (kept as an all-NaN data row, still 1:1),
  • a trailing row whose only value is the error itself (pandas sees all-NaN but keeps the row, so the #REF! lands in the right cell).

So the correctness hinges on pandas keeping every worksheet row as exactly one <tr>. That invariant holds in 2.2.x, but note it's an implementation detail of read_excel, not a contract — if pandas ever starts trimming trailing all-NaN rows (it has flip-flopped on that historically), rows[cell.row - 1] becomes an IndexError and the whole conversion fails on a file whose previous behavior was just a stray NaN. Since the errors list is usually tiny, a cheap belt-and-braces guard like if cell.row - 1 < len(rows): ... (or skipping out-of-range cells) would keep a bad sheet producing degraded-but-valid output instead of a crash. Not a blocker, just hardening.

Nit: the three cases above are exactly the ones a future pandas bump would break first — worth adding one regression test with a blank leading row so the alignment assumption is pinned in CI rather than implicit.

San-Hsien (SanHsien) added a commit to SanHsien/markitdown that referenced this pull request Oct 26, 2026
…R watermark

Ten new upstream PRs, none adopted. Four are real fixes (xlsx error
values, ipynb string source, RSS 1.0, data URI fragment) whose defects
reproduce on this fork but are outside the Windows/CJK exception class;
six are noise or re-submissions of already-adopted microsoft#1913. Reasons and
revisit triggers are in docs/DECISIONS.md. PR watermark 2765 -> 2775.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.

XLSX conversion loses Excel error values such as #DIV/0! and #REF!

2 participants