Skip to content

Stop repeat exports corrupting files which contain a table - #1745

Open
Mike-Crowley wants to merge 1 commit into
dfinke:masterfrom
Mike-Crowley:fix-1725-table-overwrite
Open

Stop repeat exports corrupting files which contain a table#1745
Mike-Crowley wants to merge 1 commit into
dfinke:masterfrom
Mike-Crowley:fix-1725-table-overwrite

Conversation

@Mike-Crowley

Copy link
Copy Markdown

Fixes #1725

Why it looked like a heisenbug

The corruption never happens on a fresh file — which is why the repro in #1725 didn't reproduce. It happens when the same export runs into an existing file, the classic regenerated-report pattern:

# run this twice (no -ClearSheet, no -Append) and the file is corrupt:
$data | Export-Excel $xlsx_path -WorksheetName "Geteilte Postfächer" -AutoSize -AutoFilter -TableStyle Light2

Two distinct illegal states get written, both confirmed by inspecting the raw sheet XML and by real Excel refusing to open the files via COM automation:

  1. Two overlapping tables. With -TableStyle but no -TableName, Add-ExcelTable always called Tables.Add(), so the second run stacked Table2 on top of Table1 over the same cells.
  2. A table plus a worksheet autofilter on the same cells. A leftover sheet autofilter from an earlier -AutoFilter export clashed with a table added later — and vice versa, -AutoFilter happily added a filter over an existing table.

Excel repairs both by stripping the table parts — which is exactly the reported "style not applied", and explains @jamesrudolph's observation that opening and saving the file once made future overwrites work (the repair removes the leftover state).

Changes

  • Add-ExcelTable: when the target range overlaps an existing table, that table is now updated instead of a new one being stacked over it (matching what the named-table path already intended). The update:
    • goes through EPPlus's Address property so its cached address stays in step with the XML — later property changes such as turning the totals row on compute from that cache;
    • keeps the extra row of a table that shows a totals row, and points the table's internal filter at the header + data rows only;
    • rebuilds the tableColumns definitions when the column count changed (Excel requires them to match the header cells exactly — a mismatch is itself a repair condition);
    • renames via the Name property when a -TableName differs from the overlapped table's name, so EPPlus's name lookup keeps working in -PassThru/-ExcelPackage sessions.
    • Tables.Delete is deliberately not used — in the bundled EPPlus 4.5.3.2 it leaves the deleted table part in the saved package (verified).
  • Export-Excel: removes a leftover worksheet autofilter that overlaps a newly created table, and the -AutoFilter branch warns and skips when an existing table already covers the data range (non-overlapping autofilters and disjoint tables are left alone).
  • New private helper Test-ExcelRangeOverlap (EPPlus's Collide is not public in 4.5).

This also repairs two related pre-existing corruptions that reproduce on master even with -TableName: re-exporting with a changed column count (ref width no longer matched the stored column definitions), and re-exporting over a table that shows a totals row (totalsRowCount was kept while the range shrank onto the data).

Verification

  • Matrix of 15 overwrite scenarios (rerun ×2/×3, alternating autofilter/table, row growth/shrink, column growth/shrink, named↔unnamed transitions, totals-row tables, in-session -PassThru chains, disjoint side-by-side tables) — every resulting file inspected at the XML level and opened in real Excel via COM; all open cleanly with exactly one correctly-styled table. Before the fix, seven of those scenarios produced files Excel refused to open.
  • 11 new Pester tests (__tests__/TableOverwrite.tests.ps1), EPPlus-only so they run on the Linux/macOS CI.
  • Full local suite: no new failures vs master (289 passed; the 5 failures present are the pre-existing Set-ItResult -Pending ones addressed by Make the CI pipeline work with Pester 5 and later #1743).
  • Fresh single-export files are unchanged in shape; two disjoint tables on one sheet still work; the -Append-to-table path still stretches the table correctly.

Known remaining gaps, unchanged from master and out of scope here: repeat exports of System.Data.DataTable input (the table is created inside EPPlus's LoadFromDataTable, which throws on collision rather than corrupting), and calling Export-Excel with table parameters but no data onto a sheet holding several disjoint tables.

🤖 Generated with Claude Code

Running the same Export-Excel command twice against one file - a
regenerated report, for example - produced a workbook Excel refused to
open ("We found a problem with some content...") whenever a table was
involved, and repairing it stripped the table style. Two causes:

- Add-ExcelTable, when no table name is given (the -TableStyle-only
  case), always added a new table, stacking a second table over the one
  a previous export created. Overlapping tables make the file invalid.
- Export-Excel combined tables and worksheet autofilters across runs: a
  leftover autofilter from an -AutoFilter export clashed with a table
  added later, and -AutoFilter added a filter over an existing table.

Both cmdlets now treat the leftover state: an export which makes a
table re-uses any table overlapping the target range - updating its
range through EPPlus's Address property so the cached address stays in
step, keeping a totals row's extra row, pointing the table's filter at
the header and data rows, rebuilding the column definitions when the
width changed (their names must match the header cells exactly), and
renaming via the Name property when -TableName differs so EPPlus's
name lookup still works - and removes a worksheet autofilter which
overlaps the new table. An -AutoFilter export warns and leaves
filtering to an overlapping table instead of corrupting the file.
EPPlus's Tables.Delete is not used: it leaves the deleted table part
in the saved package.

This also repairs two related pre-existing corruptions: re-exporting
over a table whose column count changed (previously broken even with
-TableName) and re-exporting over a table which shows a totals row.

Every scenario was verified by opening the resulting files in Excel
via COM: files which previously failed to open now open with one
correctly-styled table, and fresh single exports are byte-shape
unchanged.

Fixes dfinke#1725

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Fixes file corruption when repeatedly exporting into an existing worksheet where tables and worksheet-level autofilters can end up overlapping (Excel repairs by stripping table parts/styles, or refuses to open the file).

Changes:

  • Rework Add-ExcelTable to detect overlap with an existing table and update/rename that table in-place instead of stacking a new one over the same cells.
  • Update Export-Excel to (a) remove a leftover worksheet autofilter when creating a table over the same range, and (b) warn/skip adding a worksheet autofilter when an overlapping table already provides filtering.
  • Add Test-ExcelRangeOverlap helper plus a new Pester test suite covering repeat-export scenarios (growth/shrink, column width changes, totals-row tables, named↔unnamed transitions, and in-session -PassThru chains).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
Public/Export-Excel.ps1 Avoids writing an invalid worksheet-autofilter/table overlap during repeated exports by removing or skipping conflicting autofilters.
Public/Add-ExcelTable.ps1 Prevents overlapping tables by reusing/updating (and optionally renaming) an existing table when the target range collides.
Private/Test-ExcelRangeOverlap.ps1 Adds a small overlap predicate for EPPlus address/range objects to support collision detection (EPPlus 4.5 lacks a public collide API).
tests/TableOverwrite.tests.ps1 Adds regression tests to verify repeated exports don’t produce overlapping tables or conflicting autofilters and that table metadata stays consistent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

Export-Excel -TableStyle resulting in file errors and the style being not applied

2 participants