Stop repeat exports corrupting files which contain a table - #1745
Open
Mike-Crowley wants to merge 1 commit into
Open
Stop repeat exports corrupting files which contain a table#1745Mike-Crowley wants to merge 1 commit into
Mike-Crowley wants to merge 1 commit into
Conversation
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>
There was a problem hiding this comment.
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-ExcelTableto 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-Excelto (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-ExcelRangeOverlaphelper plus a new Pester test suite covering repeat-export scenarios (growth/shrink, column width changes, totals-row tables, named↔unnamed transitions, and in-session-PassThruchains).
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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:
-TableStylebut no-TableName,Add-ExcelTablealways calledTables.Add(), so the second run stackedTable2on top ofTable1over the same cells.-AutoFilterexport clashed with a table added later — and vice versa,-AutoFilterhappily 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:Addressproperty so its cached address stays in step with the XML — later property changes such as turning the totals row on compute from that cache;tableColumnsdefinitions when the column count changed (Excel requires them to match the header cells exactly — a mismatch is itself a repair condition);Nameproperty when a-TableNamediffers from the overlapped table's name, so EPPlus's name lookup keeps working in-PassThru/-ExcelPackagesessions.Tables.Deleteis 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-AutoFilterbranch warns and skips when an existing table already covers the data range (non-overlapping autofilters and disjoint tables are left alone).Test-ExcelRangeOverlap(EPPlus'sCollideis 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 (totalsRowCountwas kept while the range shrank onto the data).Verification
-PassThruchains, 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.__tests__/TableOverwrite.tests.ps1), EPPlus-only so they run on the Linux/macOS CI.Set-ItResult -Pendingones addressed by Make the CI pipeline work with Pester 5 and later #1743).-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.DataTableinput (the table is created inside EPPlus'sLoadFromDataTable, which throws on collision rather than corrupting), and callingExport-Excelwith table parameters but no data onto a sheet holding several disjoint tables.🤖 Generated with Claude Code