pola-rs/polars · error · DuplicateError
cannot create a second {col!r} column
Error message
cannot create a second {col!r} column What it means
polars.exceptions.DuplicateError raised while preparing an Excel write (write_excel) from _xl_inject_dummy_table_columns. Sparklines, column_formulas, and row_totals are realized by injecting placeholder columns into the frame; if one of those definitions uses a column name that already exists in the DataFrame, injecting it would create a duplicate column, which polars forbids.
Source
Thrown at py-polars/src/polars/io/spreadsheet/_write_utils.py:247
)
def _xl_inject_dummy_table_columns(
df: DataFrame,
coldefs: dict[str, Any],
*,
dtype: dict[str, PolarsDataType] | PolarsDataType | None = None,
expr: Expr | None = None,
) -> DataFrame:
"""Insert dummy frame columns in order to create empty/named table columns."""
df_original_columns = set(df.columns)
df_select_cols = df.columns.copy()
cast_lookup = {}
for col, definition in coldefs.items():
if col in df_original_columns:
msg = f"cannot create a second {col!r} column"
raise DuplicateError(msg)
elif not isinstance(definition, dict):
df_select_cols.append(col)
else:
cast_lookup[col] = definition.get("return_dtype")
insert_before = definition.get("insert_before")
insert_after = definition.get("insert_after")
if insert_after is None and insert_before is None:
df_select_cols.append(col)
else:
insert_idx = (
df_select_cols.index(insert_after) + 1 # type: ignore[arg-type]
if insert_before is None
else df_select_cols.index(insert_before)
)
df_select_cols.insert(insert_idx, col)
expr = F.lit(None) if expr is None else exprView on GitHub (pinned to df599052da)
Solutions
- Give the injected column a new, unused name (e.g. 'trend', 'total_x').
- Or drop/rename the existing DataFrame column before writing if the sheet column should be formula/sparkline-driven instead of data-driven.
- Assert upfront that set(sparklines) | set(column_formulas) | set(row_totals or []) is disjoint from set(df.columns).
Example fix
# before
pl.write_excel(df, sparklines={'total': {'columns': ['q1', 'q2', 'q3', 'q4']}}) # 'total' already in df
# after
pl.write_excel(df, sparklines={'trend': {'columns': ['q1', 'q2', 'q3', 'q4']}}) Defensive patterns
Strategy: validation
Validate before calling
injected = set(sparklines or {}) | set(column_formulas or {})
if row_totals:
injected |= {row_totals} if isinstance(row_totals, str) else set(row_totals or [])
clash = injected & set(df.columns)
if clash:
raise ValueError(f'write_excel placeholder names collide with df columns: {sorted(clash)}')
pl.write_excel(df, sparklines=sparklines, column_formulas=column_formulas, row_totals=row_totals) Try / catch
try:
pl.write_excel(df, sparklines=sparklines)
except pl.exceptions.DuplicateError as e:
if 'cannot create a second' in str(e):
# rename the colliding placeholder and retry
renamed = {f'{k}_x': v for k, v in sparklines.items() if k in df.columns}
pl.write_excel(df, sparklines={**{k: v for k, v in sparklines.items() if k not in df.columns}, **renamed})
else:
raise Prevention
- Choose placeholder names with a suffix convention unlikely to appear in data ('_trend', '_calc').
- Assert placeholder names are disjoint from df.columns before every write_excel call.
- Re-run report smoke tests whenever upstream schemas add columns.
When it happens
Trigger: pl.write_excel(df, sparklines={'total': {'columns': ['q1','q2']}}) when 'total' is already a df column; same for column_formulas={'total': {...}} or row_totals named after an existing column.
Common situations: Writing a report whose DataFrame already contains an aggregate column and trying to overlay an in-sheet sparkline/formula with the same header; schemas that evolved (a computed 'total' column was added upstream) breaking a previously working write_excel call.
Related errors
- supplying 'columns' param value is mandatory for sparklines
- sparkline data range/cols must all be adjacent
- invalid dtype_format value: {fmt!r} (expected format string,
- invalid table style key: {key!r}
- the given workbook object {wb.filename!r} is not the parent
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/9cae133237bf9f15.
Report an issue: GitHub.