Fix openpyxl "TypeError: expected <class ...>"
openpyxl models the Excel file format with typed attributes: a font size must be a number, a fill must be a Fill object, a colour must be a Color. When code assigns something else, openpyxl raises one of these:
TypeError: expected <class 'float'>
TypeError: expected <class 'openpyxl.styles.fills.Fill'>
TypeError: <class 'openpyxl.styles.fonts.Font'>.color should be <class 'openpyxl.styles.colors.Color'> but value is <class 'tuple'>
The class in the message is what openpyxl wanted; the job is to find which assignment gave it something else. Usually that is the line in the traceback. Sometimes it is not: a few mistakes are accepted silently and fail only when the workbook is saved, with a traceback that points into openpyxl's writer. This page covers both. It is part of Troubleshooting Common Python Excel Errors.
Prerequisites
pip install openpyxl
The examples use openpyxl 3.1. Each one reproduces the error, then shows the fix.
Case 1 — expected <class 'float'>: a number that is not a number
Numeric attributes such as font size, column width, row height and chart style try to convert the value with float(). A numeric string converts; a word does not:
from openpyxl import Workbook
from openpyxl.styles import Font
wb = Workbook()
ws = wb.active
for label, action in [
("Font(size='12')", lambda: Font(size="12")),
("Font(size='large')", lambda: Font(size="large")),
("column width 'wide'", lambda: setattr(ws.column_dimensions["A"], "width", "wide")),
("column width None", lambda: setattr(ws.column_dimensions["A"], "width", None)),
]:
try:
action()
print(f"{label:22} accepted")
except TypeError as e:
print(f"{label:22} TypeError: {e}")
The values usually come from a config file or a spreadsheet of settings, where "12" is fine and "large" or an empty cell is not. Validate settings when you load them, not when openpyxl rejects them. Resetting a column to its default width is del ws.column_dimensions["A"], not width = None.
Case 2 — should be <class 'Color'> but value is <class 'tuple'>
Colours are the most common source. openpyxl accepts a Color object or an aRGB hex string: six hex digits (FF0000) or eight with alpha (FFFF0000). RGB tuples raise a TypeError; CSS-style strings with # and colour names raise a ValueError:
from openpyxl.styles import PatternFill
for label, action in [
("Font(color=(255, 0, 0))", lambda: Font(color=(255, 0, 0))),
("Font(color='#FF0000')", lambda: Font(color="#FF0000")),
("PatternFill(fgColor='red')", lambda: PatternFill("solid", fgColor="red")),
("Font(color='FF0000')", lambda: Font(color="FF0000")),
]:
try:
action()
print(f"{label:28} accepted")
except (TypeError, ValueError) as e:
print(f"{label:28} {type(e).__name__}: {str(e)[:70]}")
A small converter keeps colour handling in one place and accepts whatever the rest of your code uses:
def to_argb(color):
if isinstance(color, tuple):
return "{:02X}{:02X}{:02X}".format(*color)
return color.lstrip("#").upper()
print(Font(color=to_argb((255, 0, 0))).color.rgb, Font(color=to_argb("#1f4e79")).color.rgb)
Case 3 — the error appears when saving
This is the confusing one. Assigning a string to a cell's fill is accepted without complaint, and the TypeError only appears at wb.save(), with a traceback that runs through openpyxl's stylesheet writer and never mentions your code:
import traceback
wb = Workbook()
ws = wb.active
ws["A1"] = "Total"
ws["A1"].fill = "FFFF00" # accepted here...
try:
wb.save("report.xlsx") # ...fails here
except TypeError as e:
frames = [f"{f.filename.split('site-packages/')[-1]}:{f.name}" for f in traceback.extract_tb(e.__traceback__)]
print("TypeError:", e)
print(" -> ".join(frames[-4:]))
When the traceback ends in write_stylesheet or elsewhere in openpyxl/writer, search your code for style assignments — .fill =, .border =, .font =, .alignment =, .protection = — and check that each assigns a style object. The fix is the object, but there is a catch: correcting the cell in the same session is not enough. The bad value was already added to the workbook's table of fills, and the next save fails the same way:
ws["A1"].fill = PatternFill(fill_type="solid", fgColor="FFFF00")
try:
wb.save("report.xlsx")
except TypeError as e:
print("still fails:", e)
Fix the code and rebuild the workbook from the start, which in a script simply means running it again:
wb = Workbook()
ws = wb.active
ws["A1"] = "Total"
ws["A1"].fill = PatternFill(fill_type="solid", fgColor="FFFF00")
wb.save("report.xlsx")
print("saved")
In a large script, finding the bad assignment by reading code can take a while. Scan the workbook before saving instead: every style attribute should be a style object, and style objects have a tagname, which plain strings and numbers do not:
def bad_styles(wb):
for sheet in wb.worksheets:
for row in sheet.iter_rows():
for cell in row:
for attr in ("font", "fill", "border", "alignment", "protection"):
if getattr(getattr(cell, attr), "tagname", None) is None:
yield sheet.title, cell.coordinate, attr, repr(getattr(cell, attr))
wb = Workbook()
ws = wb.active
ws["B2"], ws["C3"] = "x", "y"
ws["B2"].fill = "FFFF00"
ws["C3"].border = "thin"
print(list(bad_styles(wb)))
It names the sheet, the cell and the attribute, which is everything the save-time traceback leaves out. It cannot catch Case 4 below, where the wrong value has already been turned into a real, but wrong, style object. Run it in tests or just before save() while debugging.
Case 4 — the mistake that never errors
Worse than a late error is none at all. cell.font = "bold" is accepted and saved, because the string is quietly converted into a font whose name is "bold". The text is not bold; the file just asks for a font that does not exist:
from openpyxl import load_workbook
wb = Workbook()
ws = wb.active
ws["A1"] = "Heading"
ws["A1"].font = "bold"
wb.save("silent.xlsx")
font = load_workbook("silent.xlsx").active["A1"].font
print(f"name={font.name!r} bold={font.b}")
Style attributes take style objects: Font(bold=True), PatternFill(...), Border(...), Alignment(...). A quick way to catch these is to assert on the style you meant in a test, for example assert ws["A1"].font.b, as shown in Test Excel Output with pytest.
Case 5 — chart attributes
Chart titles accept a string, which openpyxl converts to a Title object, but not a number; that surfaces as a "should be style is a style number from 1 to 48, not a name:
from openpyxl.chart import BarChart
chart = BarChart()
for label, action in [
("title = 'Sales 2026'", lambda: setattr(chart, "title", "Sales 2026")),
("title = 2026", lambda: setattr(chart, "title", 2026)),
("style = 'modern'", lambda: setattr(chart, "style", "modern")),
("style = 10", lambda: setattr(chart, "style", 10)),
]:
try:
action()
print(f"{label:22} accepted")
except TypeError as e:
print(f"{label:22} TypeError: {str(e)[:80]}")
Convert labels with str() when they come from data: chart.title = str(year).
Values from pandas and NumPy
Settings often arrive from a DataFrame, as NumPy numbers rather than Python ones. Numeric attributes accept them, because float() converts them. Cell values accept NumPy scalars too. What fails is a container: a list or dict read from JSON config and assigned as a cell value raises a different error, a ValueError, not a TypeError:
import numpy as np
wb = Workbook()
ws = wb.active
ws.row_dimensions[1].height = np.float32(24)
ws["A1"] = np.int64(5)
print("NumPy values accepted:", ws.row_dimensions[1].height, ws["A1"].value)
try:
ws["A2"] = ["North", "South"]
except ValueError as e:
print("ValueError:", e)
That one is covered in its own page, linked below; the fix is to write one value per cell or join the list into a string.
Common pitfalls
- Reading only the last line. The class in the message tells you the kind of attribute; the traceback line above it tells you which assignment.
- Tracebacks ending in openpyxl's writer. The cause is an earlier style assignment of the wrong type; search for
.fill =and friends. - CSS colours. Strip the
#; openpyxl wants aRGB hex, not#RRGGBBor colour names. - Strings for style objects.
cell.font = "bold"saves without error and does nothing useful. - Config values as strings. Numeric strings convert, words and empty values do not; validate settings up front.
Conclusion
The class named in openpyxl's TypeError is the type the attribute expects: float for sizes, widths and chart styles, Color for colours, Fill, Font and the other style classes for cell formatting, Title for chart titles. Pass that type — style objects rather than strings, aRGB hex strings rather than tuples or # colours, numbers rather than words. When the traceback ends inside openpyxl's writer, the cause is an earlier style assignment; and remember that cell.font = "bold" fails silently, so check styles in tests.
Frequently asked questions
What does "TypeError: expected <class 'float'>" mean in openpyxl? A numeric attribute such as a font size, column width, row height or chart style received a value that float() cannot convert, such as a word or None. Pass a number.
How do I set a colour in openpyxl? Use an aRGB hex string without the hash, such as "FF0000" or "FFFF0000", or a Color object. RGB tuples and CSS names are rejected.
Why does openpyxl raise TypeError only when I save? Some style assignments, such as a string assigned to cell.fill, are stored without checking and converted when the stylesheet is written. Look for style attributes assigned a string or other plain value.
Why is my text not bold after cell.font = "bold"? The string is treated as a font name, so the cell gets a font called bold. Use cell.font = Font(bold=True).
Related
- Up one level: Troubleshooting Common Python Excel Errors — the other common errors.
- Fix "Excel Found Unreadable Content" After Writing with Python — when a file saves but Excel complains.
- Fix "ValueError: Cannot convert to Excel" in openpyxl — the cell value equivalent.
- Test Excel Output with pytest — catching silent style mistakes.
- Fix openpyxl max_row Counting Empty Rows — another openpyxl surprise.
More on errors & fixes
- FixHandle Permission Denied When Writing Excel in Python
- How-toWhat openpyxl Loses When You Save an Existing Workbook
- FixFix Excel Serial Numbers Showing Instead of Dates
- How-toHandle Daylight Saving Time in Excel Timestamps
- How-toHandle Microsoft Graph Throttling in Excel Automation
- GuideError Handling and Logging in Excel Automation