pola-rs/polars · error · TypeError

invalid dtype_format value: {fmt!r} (expected format string,

Error message

invalid dtype_format value: {fmt!r} (expected format string, got {qualified_type_name(fmt)!r})

What it means

TypeError raised by _xl_setup_table_options (write_excel preparation) when a dtype_formats value is not a string. dtype_formats maps data types (or tuples/frozensets of types, which get expanded) to Excel number-format strings such as '#,##0.00'; passing an int, function, or other object instead of a format string is rejected.

Source

Thrown at py-polars/src/polars/io/spreadsheet/_write_utils.py:457

    column_formulas = {
        col: {"formula": options} if isinstance(options, str) else options
        for col, options in (formulas or {}).items()
    }

    # normalise formats
    column_formats = dict(column_formats or {})
    dtype_formats = dict(dtype_formats or {})

    for tp in list(dtype_formats):
        if isinstance(tp, (tuple, frozenset)):
            updates: dict[OneOrMoreDataTypes, str] = dict.fromkeys(
                tp, dtype_formats.pop(tp)
            )
            dtype_formats.update(updates)
    for fmt in dtype_formats.values():
        if not isinstance(fmt, str):
            msg = f"invalid dtype_format value: {fmt!r} (expected format string, got {qualified_type_name(fmt)!r})"
            raise TypeError(msg)

    # inject sparkline/row-total placeholder(s)
    if sparklines:
        df = _xl_inject_dummy_table_columns(df, sparklines)
    if column_formulas:
        df = _xl_inject_dummy_table_columns(df, column_formulas)
    if row_totals:
        df = _xl_inject_dummy_table_columns(df, row_total_funcs, dtype=row_totals_dtype)

    # seed format cache with default fallback format
    fmt_default = format_cache.get({"valign": "vcenter"})

    if table_style is None:
        # no table style; apply default black (+ve) & red (-ve) numeric formatting
        int_base_fmt = _XL_DEFAULT_INTEGER_FORMAT_
        flt_base_fmt = _XL_DEFAULT_FLOAT_FORMAT_
    else:
        # if we have a table style, defer the colours to that style

View on GitHub (pinned to df599052da)

Solutions

  1. Use proper Excel format strings: dtype_formats={pl.Float64: '#,##0.0000', pl.Date: 'yyyy-mm-dd'}.
  2. For per-column control use column_formats={'amount': '#,##0.00'} instead of dtype-level formats.
  3. If the format is dynamic, build it as an f-string that resolves to a str before passing.

Example fix

# before
pl.write_excel(df, dtype_formats={pl.Float64: 4, pl.Date: to_datestr})

# after
pl.write_excel(df, dtype_formats={pl.Float64: '0.0000', pl.Date: 'yyyy-mm-dd'})
Defensive patterns

Strategy: type-guard

Validate before calling

for tp, fmt in dtype_formats.items():
    if not isinstance(fmt, str):
        raise TypeError(f'dtype_formats[{tp!r}] must be an Excel format string, got {type(fmt).__name__}')
pl.write_excel(df, dtype_formats=dtype_formats)

Type guard

def is_valid_dtype_formats(d) -> bool:
    return isinstance(d, dict) and all(isinstance(v, str) for v in d.values())

Try / catch

try:
    pl.write_excel(df, dtype_formats=dtype_formats)
except TypeError as e:
    if 'invalid dtype_format value' in str(e):
        pl.write_excel(df, dtype_formats={k: str(v) for k, v in dtype_formats.items()})
    else:
        raise

Prevention

When it happens

Trigger: pl.write_excel(df, dtype_formats={pl.Float64: 4}) (intended '0.0000'), or dtype_formats={pl.Date: some_formatter_callable}. Values are checked with isinstance(fmt, str) after tuple/frozenset keys are expanded.

Common situations: Confusing Excel format strings with Python format specs or pandas float_format callables; passing {'0000'} style or numeric precision directly; copying config from openpyxl number_format usage where non-string values sometimes slipped through.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/a6f062cbadd41d27. Report an issue: GitHub.