Skip to content

Workbook contains no default style, apply openpyxl's default

Have you ever seen this UserWarning before?

import pandas as pd
df = pd.read_excel('some-file.xlsx')
# UserWarning: Workbook contains no default style, apply openpyxl's default

Short answer: ignore it. Your data is fine.

That is not a guess. Reading the same file with and without a stylesheet
produces frames that compare equal — the warning is about formatting
information openpyxl could not find, and pandas throws formatting away anyway.

If you want the noise gone, skip to Silencing it below. If you want to know
why it happened, read on.

What the warning actually means

An .xlsx file is a zip archive. One of the files inside it, xl/styles.xml,
describes fonts, colors, number formats — and a list of named cell styles,
of which the most important is the one Excel calls Normal.

openpyxl expects to find that list. When the stylesheet is present but declares
no named styles, openpyxl supplies its own default and tells you it did. From
openpyxl/styles/stylesheet.py:

if not wb._named_styles:
    normal = styles['Normal']
    wb.add_named_style(normal)
    warn("Workbook contains no default style, apply openpyxl's default")

That is the entire mechanism. Nothing has failed. openpyxl filled a gap and
announced it.

Which files trigger it

Files that Excel did not write. Exports from BI tools, government statistics
portals, database reporting layers, Java's Apache POI, and anything that
assembles the XML directly tend to omit the parts of the format nobody reads —
including the named-styles list.

This is why the warning shows up so often in data analysis and almost never when
you open your own spreadsheets: the files come from a system, not a person.

You can manufacture one to see it happen. Take any workbook, remove the
<cellStyles> element from its stylesheet, and read it back:

import re, zipfile
import pandas as pd

pd.DataFrame({'a': [1, 2]}).to_excel('normal.xlsx', index=False)

with zipfile.ZipFile('normal.xlsx') as z:
    styles = z.read('xl/styles.xml').decode()
stripped = re.sub(r'<cellStyles.*?</cellStyles>', '', styles, flags=re.S)

with zipfile.ZipFile('normal.xlsx') as zin, \
     zipfile.ZipFile('nostyle.xlsx', 'w') as zout:
    for item in zin.infolist():
        data = zin.read(item.filename)
        zout.writestr(item, stripped.encode()
                      if item.filename == 'xl/styles.xml' else data)

pd.read_excel('nostyle.xlsx')
# UserWarning: Workbook contains no default style, apply openpyxl's default

And the proof that nothing is lost:

pd.read_excel('nostyle.xlsx').equals(pd.read_excel('normal.xlsx'))
# True

engine='openpyxl' does not fix it

This is the most commonly repeated advice about this warning, and it is wrong:

pd.read_excel('nostyle.xlsx', engine='openpyxl')
# UserWarning: Workbook contains no default style, apply openpyxl's default

Of course it doesn't. openpyxl is already the engine pandas chose for .xlsx;
naming it explicitly changes nothing about which code runs. The warning comes
from inside openpyxl, so asking for openpyxl more loudly cannot help.

Silencing it

Switch engines. calamine is a different reader entirely — a Rust library
with no opinion about named styles — so the warning never arises:

pd.read_excel('nostyle.xlsx', engine='calamine')   # no warning

Install it with pip install python-calamine. It is also considerably faster
than openpyxl on large files, so this is worth knowing about for its own sake.
Verify the values match on a file you care about before switching; it is a
different parser, not a drop-in guarantee.

Or suppress the warning. Scope it as narrowly as you can stand — a blanket
ignore will hide the next warning too, and the next one might matter:

import warnings

with warnings.catch_warnings():
    warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl')
    df = pd.read_excel('some-file.xlsx')

The module='openpyxl' argument is what keeps this honest. It silences
openpyxl and nothing else, and only for the duration of the with block.

It repeats on every read

Python often shows a given warning once and then stays quiet, which is why
people assume the problem "went away." This one does not behave that way:

for path in paths:            # five files
    pd.read_excel(path)
# five warnings

Five reads, five warnings. If you are looping over a directory of exports, your
output fills up. That is usually the point at which people go looking for this
page — not the first warning, but the fiftieth.

The other one: "no stylesheet"

There is a sibling warning with a different cause:

UserWarning: Workbook contains no stylesheet, using openpyxl's defaults

That fires when xl/styles.xml is missing altogether, rather than present and
incomplete. Same verdict — cosmetic, data intact — and the same fixes apply.

Where this comes up in practice

Government and NGO statistical releases are the usual source, which is why this
warning is a regular visitor in Bamboo Weekly exercises. See
BW #19: Working women for a real example, and read_excel in pandas for the arguments that actually change what you get back.