{"record":{"id":"a6f062cbadd41d27","repo":"pola-rs/polars","slug":"invalid-dtype-format-value-fmt-r-expected-form","errorCode":null,"errorMessage":"invalid dtype_format value: {fmt!r} (expected format string, got {qualified_type_name(fmt)!r})","messagePattern":"invalid dtype_format value: (.+?) \\(expected format string, got (.+?)\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/spreadsheet/_write_utils.py","lineNumber":457,"sourceCode":"    column_formulas = {\n        col: {\"formula\": options} if isinstance(options, str) else options\n        for col, options in (formulas or {}).items()\n    }\n\n    # normalise formats\n    column_formats = dict(column_formats or {})\n    dtype_formats = dict(dtype_formats or {})\n\n    for tp in list(dtype_formats):\n        if isinstance(tp, (tuple, frozenset)):\n            updates: dict[OneOrMoreDataTypes, str] = dict.fromkeys(\n                tp, dtype_formats.pop(tp)\n            )\n            dtype_formats.update(updates)\n    for fmt in dtype_formats.values():\n        if not isinstance(fmt, str):\n            msg = f\"invalid dtype_format value: {fmt!r} (expected format string, got {qualified_type_name(fmt)!r})\"\n            raise TypeError(msg)\n\n    # inject sparkline/row-total placeholder(s)\n    if sparklines:\n        df = _xl_inject_dummy_table_columns(df, sparklines)\n    if column_formulas:\n        df = _xl_inject_dummy_table_columns(df, column_formulas)\n    if row_totals:\n        df = _xl_inject_dummy_table_columns(df, row_total_funcs, dtype=row_totals_dtype)\n\n    # seed format cache with default fallback format\n    fmt_default = format_cache.get({\"valign\": \"vcenter\"})\n\n    if table_style is None:\n        # no table style; apply default black (+ve) & red (-ve) numeric formatting\n        int_base_fmt = _XL_DEFAULT_INTEGER_FORMAT_\n        flt_base_fmt = _XL_DEFAULT_FLOAT_FORMAT_\n    else:\n        # if we have a table style, defer the colours to that style","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/spreadsheet/_write_utils.py#L439-L475","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use proper Excel format strings: dtype_formats={pl.Float64: '#,##0.0000', pl.Date: 'yyyy-mm-dd'}.","For per-column control use column_formats={'amount': '#,##0.00'} instead of dtype-level formats.","If the format is dynamic, build it as an f-string that resolves to a str before passing."],"exampleFix":"# before\npl.write_excel(df, dtype_formats={pl.Float64: 4, pl.Date: to_datestr})\n\n# after\npl.write_excel(df, dtype_formats={pl.Float64: '0.0000', pl.Date: 'yyyy-mm-dd'})","handlingStrategy":"type-guard","validationCode":"for tp, fmt in dtype_formats.items():\n    if not isinstance(fmt, str):\n        raise TypeError(f'dtype_formats[{tp!r}] must be an Excel format string, got {type(fmt).__name__}')\npl.write_excel(df, dtype_formats=dtype_formats)","typeGuard":"def is_valid_dtype_formats(d) -> bool:\n    return isinstance(d, dict) and all(isinstance(v, str) for v in d.values())","tryCatchPattern":"try:\n    pl.write_excel(df, dtype_formats=dtype_formats)\nexcept TypeError as e:\n    if 'invalid dtype_format value' in str(e):\n        pl.write_excel(df, dtype_formats={k: str(v) for k, v in dtype_formats.items()})\n    else:\n        raise","preventionTips":["Excel formats are strings ('#,##0.00', 'yyyy-mm-dd') — never callables or ints.","Centralize format strings as named constants (FMT_MONEY = '#,##0.00').","Use column_formats for per-column control and keep dtype_formats keys as dtypes only."],"tags":["excel","spreadsheet","write-excel","formatting","type-error"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}