Fix
Getting Started With Python Excel AutomationDeep dive

Fix Wrong Sheet Order After Writing with pandas

Sheets come out as Apr, Aug, Dec, Feb… or the summary ends up last after appending to a workbook. Find which of the usual causes applies — alphabetical group keys, sets and unsorted dictionaries, sheets appended to an existing file — write the sheets in the order you want, or reorder an existing workbook with openpyxl, set the sheet that opens first, and check the final order in a test.

A report written with pandas has one sheet per month, and they come out as Apr, Aug, Dec, Feb, Jan… Or a summary sheet added to an existing workbook ends up at the far right, after twelve monthly tabs, where nobody will find it. The data is right; the order is wrong, and for a workbook people navigate by tabs that matters almost as much. pandas writes sheets in exactly the order your code writes them, and Excel shows tabs in the order they are stored, so a wrong order always has a cause in the code that produced the sheets — usually one of three. This page reproduces each cause, shows how to write the sheets in the intended order in the first place, how to reorder an existing workbook with openpyxl when that is not possible, and how to make sure the right sheet opens first. It belongs to Troubleshooting Common Python Excel Errors.

Prerequisites

Bash
pip install pandas openpyxl

The input is a year of daily sales. The report should have a Summary sheet first, then one sheet per month from January to December.

Cause 1 — Group keys that sort alphabetically

Python
import pandas as pd

sales = pd.read_excel("daily_sales.xlsx")
sales["Month"] = sales["Date"].dt.strftime("%b")

with pd.ExcelWriter("by_month_wrong.xlsx") as xw:
    for month, g in sales.groupby("Month"):
        g.to_excel(xw, sheet_name=month, index=False)
with pd.ExcelFile("by_month_wrong.xlsx") as f:
    print(f.sheet_names)

groupby sorts its keys by default, and month names are strings, so they sort alphabetically: April, August, December, February and so on. The sheets are written in that order and Excel shows them in that order. The output shows exactly that: Apr, Aug, Dec, Feb, Jan, Jul, Jun, Mar, May, Nov, Oct, Sep — every month present, none in the right place. The fix is to group by something that sorts in time — the month number or a monthly period — and use the name only for the sheet title:

Python
sales["Period"] = sales["Date"].dt.to_period("M")
with pd.ExcelWriter("by_month.xlsx") as xw:
    for period, g in sales.groupby("Period"):
        g.drop(columns=["Month", "Period"]).to_excel(xw, sheet_name=period.strftime("%b %Y"), index=False)
with pd.ExcelFile("by_month.xlsx") as f:
    print(f.sheet_names)

A monthly Period sorts chronologically and also carries the year, which avoids a second problem when the data spans more than one year: two sheets called Jan for different years. The same applies to any categorical order that is not alphabetical — sizes, priority levels, regions in a fixed reporting order: group by an ordered key, such as an ordered Categorical, or loop over an explicit list.

Grouping by name or by period Grouping by month name sorts the keys alphabetically, giving April, August, December and so on. Grouping by a monthly period sorts them in time, January to December, and keeps the year in the sheet name. groupby("Month") — names Apr, Aug, Dec, Feb, Jan… alphabetical order Jan of two years collide groupby("Period") — periods Jan 2026, Feb 2026, … chronological order year in every name

Cause 2 — Sets and unordered collections

Python
regions = {"North", "South", "East", "West"}            # a set: no defined order
print(list(regions))
ORDER = ["North", "South", "East", "West"]               # the order the business reports in
print([r for r in ORDER if r in regions])

Sheet names collected in a set — often from set(df["Region"]) or df["Region"].unique() passed through a set to remove duplicates — come back in an order that has nothing to do with how the business reads the report, and string sets can even change order between runs, because Python randomises string hashing per process. Dictionaries keep insertion order, so a dictionary built in the right order is safe; a set is not. When the order matters, loop over an explicit list and keep only the names that occur in the data, as the second line does — the result is the same on every run, and a region that appears in the data but not in the list can be reported rather than placed somewhere arbitrary.

Cause 3 — Appending to an existing workbook

Python
summary = sales.groupby("Period")["Revenue"].sum().rename_axis("Month").reset_index()
summary["Month"] = summary["Month"].dt.strftime("%b %Y")
with pd.ExcelWriter("by_month.xlsx", mode="a", engine="openpyxl", if_sheet_exists="replace") as xw:
    summary.to_excel(xw, sheet_name="Summary", index=False)
with pd.ExcelFile("by_month.xlsx") as f:
    print(f.sheet_names[:3], "…", f.sheet_names[-2:])

mode="a" opens an existing workbook and adds to it, and a new sheet goes at the end — so a summary added to a finished monthly workbook lands after December. pandas has no option to insert a sheet at a position. (Replacing a sheet that already exists with if_sheet_exists="replace" keeps it where it was in current pandas versions; it is new sheets that are added at the end.) The simplest fix is to write the workbook in one pass with the summary first. When the workbook already exists and must be updated rather than rewritten, reorder it afterwards.

Step 4 — Reorder an existing workbook with openpyxl

Python
from openpyxl import load_workbook

wb = load_workbook("by_month.xlsx")
wb.move_sheet("Summary", offset=-wb.sheetnames.index("Summary"))      # move to the front
wb.active = 0                                                          # open on the summary
for ws in wb.worksheets:
    ws.sheet_view.tabSelected = ws is wb.active                        # only one tab selected
wb.save("by_month.xlsx")
print(load_workbook("by_month.xlsx").sheetnames[:4])

move_sheet moves one sheet by an offset, so moving it to the front is an offset of minus its current position. For a full reorder — say, a desired list of names — sorting the workbook's internal list by position in that list does it in one line, wb._sheets.sort(key=desired.index); the underscore marks it as internal to openpyxl, but it is the documented way people reorder many sheets at once and it is stable across versions. After moving sheets, set wb.active to the sheet the reader should see first, and make sure only that tab is marked selected: a workbook saved with several tabs selected opens in Excel with the sheets grouped, and anything typed then goes into all of them.

Getting the order right The best fix is to write sheets in the intended order in one pass. If the workbook already exists, reorder it with openpyxl, by moving one sheet or sorting the whole list, then set the active sheet and make sure only one tab is selected. Write in order one pass, summary first Existing workbook? reorder with openpyxl move_sheet / sort one sheet or all Set active sheet the one to open on One tab selected avoid grouped sheets

With XlsxWriter, and when other code reads by position

With the XlsxWriter engine the same rule holds even more strictly: sheets appear in the order they are added, and because XlsxWriter only writes new files, there is no reordering afterwards — the fix is always to add them in the right order. Its activate() method chooses the sheet that opens first, and set_first_sheet() chooses which tab is scrolled into view at the left of the tab bar, which matters in a workbook with more tabs than fit on screen.

Sheet order also matters to code that reads the workbook later. pd.read_excel(path, sheet_name=0) reads the first sheet, whatever it is called, so moving a summary to the front silently changes what such a script reads. Downstream code should read sheets by name — sheet_name="Summary" — which keeps working whatever order the tabs end up in, and is one more reason to fix the order deliberately rather than leave it to chance. Hidden sheets keep their position in the order too, so a hidden lookup sheet at the front would become sheet 0 for any script that reads by position.

Step 5 — Test the order

Python
expected = ["Summary"] + [p.strftime("%b %Y") for p in pd.period_range("2026-01", "2026-12", freq="M")]
actual = load_workbook("by_month.xlsx", read_only=True).sheetnames
assert actual == expected, f"sheet order wrong: {actual}"
print("sheet order OK:", actual[:3], "…", actual[-1])

Sheet order is easy to break again later — someone adds a sheet in the middle of the code, or changes a group key — so it is worth a one-line assertion in the report's tests: the expected list of sheet names, built from the same rules as the report, compared with the workbook's sheetnames. Opening the file in read-only mode makes the check fast even for large workbooks. The broader approach to testing report output is in Test Excel Output with pytest.

The final tab order The workbook opens on the Summary sheet, followed by one sheet per month from Jan 2026 to Dec 2026 in calendar order. A B C D E F 1 1 2 3 4 … 13 2 Summary Jan 2026 Feb 2026 Mar 2026 … Dec 2026

Common pitfalls

  • Grouping by month names. Group by a period or month number; use the name only as the title.
  • Collecting sheet names in a set. Loop over an explicit, ordered list.
  • Appending a summary with mode="a". It goes last; write in one pass or reorder.
  • Forgetting the active sheet. Set wb.active after reordering.
  • Several tabs selected. Clear tabSelected on all but the active sheet.

Conclusion

pandas writes sheets in the order the code writes them, so a wrong tab order comes from the code: alphabetical group keys such as month names, unordered sets of names, or sheets appended to an existing workbook. Group by periods or loop over an explicit list to write in the right order, write the summary first in a single pass, or reorder an existing workbook with openpyxl's move_sheet or by sorting its sheet list; then set the active sheet and assert the order in a test.

Frequently asked questions

Why are my monthly sheets in alphabetical order?groupby sorts month names as text; group by a monthly period or month number instead.

How do I move a sheet to the front with openpyxl? Use wb.move_sheet("Summary", offset=-wb.sheetnames.index("Summary")).

Why does a new sheet appear at the end when I append with pandas?ExcelWriter(mode="a") adds new sheets after the existing ones; reorder with openpyxl afterwards or write the file in one pass.

How do I choose which sheet opens first? Set wb.active to the sheet's index and make sure only that sheet has tabSelected set.

More on errors & fixes

More on errors & fixes →