larksuite/cli · error · ValueError

column labels collide after str() conversion; rename the Dat

Error message

column labels collide after str() conversion; rename the DataFrame columns before packing

What it means

df_to_sheet serializes a pandas DataFrame into the Feishu sheet protocol, which requires string column names. Because every column label is converted with str(), two distinct labels can normalize to the same string (e.g. integer 0 and string '0', or True and 'True'); the packed JSON would then contain duplicate columns and the dtype lookup would silently merge them, so the helper refuses to continue with this ValueError.

Source

Thrown at skills/lark-sheets/scripts/sheets_df.py:34

import pandas as pd


def df_to_sheet(df, name, formats=None):
    """Pack one DataFrame into one entry of a `+table-put --sheets` payload."""
    packed = json.loads(df.to_json(orient="split", date_format="iso"))
    # The protocol requires string column names. pandas keeps integer labels
    # (e.g. the default RangeIndex columns 0/1/2) as JSON numbers, while the
    # dtypes dict keys get stringified during JSON serialization — the CLI
    # then rejects `columns` ("cannot unmarshal number into … type string")
    # and the dtype lookup would miss anyway. Stringify every key once, and
    # refuse to continue when that conversion silently merges two columns.
    normalized_labels = [str(c) for c in df.columns]
    columns = [str(c) for c in packed["columns"]]
    if normalized_labels != columns:
        columns = normalized_labels
    if len(set(columns)) != len(columns):
        raise ValueError(
            "column labels collide after str() conversion; "
            "rename the DataFrame columns before packing"
        )
    packed["columns"] = columns
    dtype_values = list(df.dtypes)
    return {
        "name": name,
        **packed,
        "dtypes": {key: str(dtype) for key, dtype in zip(columns, dtype_values)},
        **({"formats": {str(k): v for k, v in formats.items()}} if formats else {}),
    }


def sheet_to_df(sheet):
    """Restore one `+table-get` sheet dict into a typed DataFrame."""
    return pd.DataFrame(sheet["data"], columns=sheet["columns"]).astype(sheet["dtypes"])

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Rename the DataFrame columns so every label is a unique string before calling df_to_sheet, e.g. df.columns = [str(c) for c in df.columns] followed by disambiguating duplicates.
  2. Audit df.columns for collisions: check that [str(c) for c in df.columns] has the same length as set(...) and fix the offending labels.
  3. Cast numeric/boolean column labels to unique prefixed strings at data-load time (e.g. 'col_0', 'col_1') so str() conversion cannot merge them.
  4. If duplicates are intentional data, pivot or transpose so they become row values instead of column labels.

Example fix

// before
df = pd.DataFrame([[1, 2]], columns=[0, "0"])
packed = df_to_sheet(df, "t")  # ValueError: labels collide after str()

// after
df = pd.DataFrame([[1, 2]], columns=["count_a", "count_b"])
# or programmatic dedupe:
df.columns = [f"col_{c}" for c in range(df.shape[1])]
packed = df_to_sheet(df, "t")
Defensive patterns

Strategy: validation

Validate before calling

labels = [str(c) for c in df.columns]
if len(set(labels)) != len(labels):
    dupes = sorted({c for c in labels if labels.count(c) > 1})
    raise ValueError(f"columns collide after str(): {dupes}")
# optionally apply proactively:
# df.columns = labels

Try / catch

try:
    packed = df_to_sheet(df, name)
except ValueError as err:
    if "column labels collide" in str(err):
        df = df.copy()
        df.columns = [f"col_{i}_{c}" for i, c in enumerate(df.columns)]
        packed = df_to_sheet(df, name)
    else:
        raise

Prevention

When it happens

Trigger: Calling df_to_sheet(df, name) where df.columns contains labels that map to the same string under str(): mixed int/str labels like 0 and '0', bool True alongside 'True', float 1.0 with '1.0', or any two labels whose str() representations are equal.

Common situations: Building DataFrames from mixed-type sources (JSON payloads, Excel imports with numeric headers, RangeIndex default columns combined with renamed string columns) before packing; merging DataFrames where one side has numeric column labels and the other string labels.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/df1ba3affab792e7. Report an issue: GitHub.