Fix
Getting Started With Python Excel AutomationDeep dive

Fix "Workbook contains no default style" Warning in openpyxl

openpyxl warns "Workbook contains no default style, apply openpyxl's default" when a file's stylesheet lacks a Normal style. The data is fine — but the related "no stylesheet" warning can turn dates into numbers. Tell them apart, silence the harmless one precisely and repair the files.

Reading a workbook exported by another system prints this, sometimes once per file in a batch job's log:

Text
UserWarning: Workbook contains no default style, apply openpyxl's default

It is a warning, not an error, and in most cases the DataFrame is exactly what you expected. The file's stylesheet — the xl/styles.xml part that holds fonts, fills and number formats — is missing the entry for Excel's "Normal" style, and openpyxl fills the gap with its own. Programs that are not Excel often write a minimal stylesheet like this. There is a close cousin, though, that is not harmless, and the two are easy to confuse. This page, part of Troubleshooting Common Python Excel Errors, shows how to tell them apart and what to do about each.

Two stylesheet warnings with different consequences Two panels. "No default style" means the Normal style entry is missing: values, number formats and dates all read correctly, and the warning is safe to silence. "No stylesheet" means the cell formats are missing: numbers read correctly but date cells arrive as serial numbers, which must be converted. Workbook contains no default style Normal style entry missing number formats present dates read as dates safe to silence Workbook contains no stylesheet cell formats missing dates arrive as numbers numbers and text fine convert date columns

Prerequisites

Bash
pip install pandas openpyxl
pip install python-calamine   # optional, for Step 4

The examples use two small files that reproduce each case: export.xlsx, whose stylesheet lacks the Normal style, and bare.xlsx, whose stylesheet lacks cell formats altogether. Both hold the same two rows: a posting date and an amount.

Step 1 — Check which warning you have and what it touched

Capture the warnings rather than reading them off the console, so you can see the exact text and compare the data from each file:

Python
import warnings
import pandas as pd

for name in ("export.xlsx", "bare.xlsx"):
    with warnings.catch_warnings(record=True) as caught:
        warnings.simplefilter("always")
        frame = pd.read_excel(name)
    print(name, [str(w.message) for w in caught])
    print(frame.to_dict("records"))

export.xlsx warns about the default style and still gives you Timestamp('2026-03-01 00:00:00'). bare.xlsx warns that there is no stylesheet and gives you 46082 — the date's serial number. A number format is what marks a cell as a date in an .xlsx file; the value underneath is always a plain number. Remove the formats and the reader has no way to know those numbers were ever dates.

Look inside the stylesheet

An .xlsx file is a zip archive, and the stylesheet is one XML file inside it. Opening it directly shows which section is missing, which is useful evidence when you report the problem to whoever produces the files. The two sections that matter here are cellXfs, the list of cell formats that every styled cell points into, and cellStyles, the list of named styles such as Normal:

Python
import re
import zipfile

def stylesheet_report(path):
    with zipfile.ZipFile(path) as z:
        if "xl/styles.xml" not in z.namelist():
            return "no styles.xml part at all"
        xml = z.read("xl/styles.xml").decode("utf-8")
    parts = {}
    for tag in ("numFmts", "cellXfs", "cellStyles"):
        m = re.search(rf'<{tag} count="(\d+)"', xml)
        parts[tag] = int(m.group(1)) if m else "missing"
    return parts

for name in ("source.xlsx", "export.xlsx", "bare.xlsx"):
    print(name, stylesheet_report(name))

The file Excel or pandas writes has all three sections. export.xlsx is missing only cellStyles — the Normal style — which is what the default-style warning reports. bare.xlsx is missing cellXfs, so no cell can point at a date format, and that is why its dates come back as plain numbers. The report takes a fraction of a second, so it is cheap to run over a whole folder of exports when you want to know how many files are affected before deciding between a filter and a conversion.

Step 2 — Silence the harmless warning precisely

For the default-style case, filter the warning — but only that warning, from that module. A blanket warnings.filterwarnings("ignore") also hides the ones you need, including the no-stylesheet warning that tells you dates are about to go wrong:

Python
warnings.filterwarnings(
    "ignore",
    message="Workbook contains no default style",
    category=UserWarning,
    module="openpyxl",
)

print(pd.read_excel("export.xlsx").dtypes.to_dict())

message is a regular expression matched against the start of the warning text, and module is matched against the module that raised it — here openpyxl.styles.stylesheet, which the pattern openpyxl covers. Put the filter at the top of the script's entry point, not inside a library function, so it is visible to whoever reads the job next.

When only one call should be quiet, scope the filter to it with a context manager instead:

Python
with warnings.catch_warnings():
    warnings.filterwarnings("ignore", message="Workbook contains no default style",
                            category=UserWarning, module="openpyxl")
    ledger = pd.read_excel("export.xlsx")
print(len(ledger))

Step 3 — Repair dates when the stylesheet is missing

For the no-stylesheet case, convert the affected columns yourself. Excel's serial numbers count days from 30 December 1899, so pd.to_datetime with that origin and unit="D" turns them back into dates:

Python
bare = pd.read_excel("bare.xlsx")
print(bare.dtypes.to_dict())

bare["posted"] = pd.to_datetime(bare["posted"], unit="D", origin="1899-12-30")
print(bare.to_dict("records"))

Convert only columns you know are dates. A quantity column holding 46082 is a quantity, and there is nothing in the file to distinguish it. If the export's layout is fixed, list the date columns in the job's configuration and convert them by name; Fix Excel Serial Numbers Showing Instead of Dates covers the conversion in depth, including time fractions and the 1904 date system.

Restoring dates from a file with no cell formats Left: the posted column read from bare.xlsx holds serial numbers 46082 and 46083. Right: after pd.to_datetime with unit D and origin 1899-12-30, the same column holds the dates 2026-03-01 and 2026-03-02. A B 1 posted amount 2 46082 120.5 3 46083 88 A B 1 posted amount 2 2026-03-01 120.5 3 2026-03-02 88 read from bare.xlsx after conversion to_datetime(unit="D")

Step 4 — Repair the file, or read it with a different engine

If the file is an intermediate you control — something your pipeline downloads and then processes in several steps — repair it once. Loading with openpyxl and saving writes a complete stylesheet, including a Normal style, so later readers see no warning:

Python
from openpyxl import load_workbook

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    wb = load_workbook("export.xlsx")
wb.save("export_repaired.xlsx")

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    pd.read_excel("export_repaired.xlsx")
print(len(caught))
# 0

Resaving does not bring back lost date formats in the no-stylesheet case — the information is not in the file to restore. It only stops the warning.

The calamine engine reads the workbook without going through openpyxl's stylesheet code, so it does not emit the warning at all. It is also considerably faster, which makes it a sensible default for batch jobs that only need values:

Python
try:
    print(pd.read_excel("export.xlsx", engine="calamine").to_dict("records"))
except ImportError:
    print("pip install python-calamine to use this engine")

calamine sees the same file, so the no-stylesheet case still gives serial numbers — switching engines hides the warning but not the underlying problem.

Step 5 — Report it upstream

If the files come from a system your organisation runs, tell its owners. A minimal stylesheet is valid enough for Excel to open, which is why nobody on their side has noticed, but it costs every consumer a workaround — and the no-stylesheet variant silently strips date information that downstream reports depend on. A one-line bug report with the warning text and a sample file is usually enough.

Handling a stylesheet warning in a job A decision chart. For "no default style" with correct data, filter that warning by message and module. For "no stylesheet", convert known date columns from serial numbers. For an intermediate file you control, load and save it with openpyxl to write a full stylesheet. For large batches that only need values, read with the calamine engine. Which warning, and whose file? no default style Filter it narrowly by message and module no stylesheet Convert date columns unit="D", origin 1899-12-30 your own intermediate Resave with openpyxl writes a full stylesheet big batch Use calamine no warning and faster

Common pitfalls

  • warnings.filterwarnings("ignore") with no arguments. It hides every warning in the process, including pandas deprecation notices and the no-stylesheet warning that signals lost dates.
  • Filtering on the wrong module. The warning is raised inside openpyxl, not pandas; a filter with module="pandas" does nothing.
  • Converting every numeric column to dates. Only columns known to be dates should be converted; the file gives no way to tell them apart.
  • Treating the warning as a sign of corruption. It is not; the file opens in Excel and the values are correct. Do not route these files to a repair or quarantine path on the strength of the warning alone.
  • Assuming a resave restores formats. It writes a valid stylesheet, but formats that were never in the file stay missing.

Performance notes

The warning itself costs nothing measurable. In a job that reads thousands of such files, the cost is log noise — one line per file — which is reason enough to filter it. If the job is slow, the read is the bottleneck, not the warning; switching to calamine removes both.

Conclusion

"Workbook contains no default style" means the file's stylesheet lacks a Normal style; the data is intact and a filter on that message and module is the right response. "Workbook contains no stylesheet" means number formats are missing, so date columns arrive as serial numbers and must be converted with unit="D" and origin 1899-12-30. Resave intermediates you control with openpyxl, use calamine for fast value-only reads, and tell the producing system's owners.

Frequently asked questions

Is the "no default style" warning dangerous? No. The file's stylesheet is missing its Normal style entry, so openpyxl adds its own. Values, number formats and dates are read correctly. It is safe to silence it with a narrow filter.

How is "no stylesheet" different? That warning means the file has no usable cell formats at all. openpyxl can then no longer tell which numbers are dates, so date columns arrive as serial numbers such as 46082. Convert those columns explicitly.

Why does pandas show the warning? pandas uses openpyxl to read .xlsx files, and openpyxl raises the warning while loading the stylesheet. The warning comes from openpyxl's stylesheet module, which is why the filter must target that module.

Can I fix the file itself? Yes. Loading it with openpyxl and saving it again writes a complete stylesheet, and the warning disappears for every later reader. Do this when the file is an intermediate you control, not when it is someone else's original.

More on errors & fixes

More on errors & fixes →