pola-rs/polars · error · TypeError

"pad_start" expects a `str`, given a {qualified_type_name(fi

Error message

"pad_start" expects a `str`, given a {qualified_type_name(fill_char)!r}

What it means

Raised in the Python layer of Expr.str.pad_start before the call reaches the Rust engine: the fill_char argument must be a plain str. Any other type (int, bytes, None, an Expr) is rejected with a TypeError that names the qualified type of the offending value. The engine only accepts a string fill character, so this is a hard precondition on the argument, not a data-dependent runtime error.

Source

Thrown at py-polars/src/polars/expr/string.py:899

        --------
        >>> df = pl.DataFrame({"a": ["cow", "monkey", "hippopotamus", None]})
        >>> df.with_columns(padded=pl.col("a").str.pad_start(8, "*"))
        shape: (4, 2)
        ┌──────────────┬──────────────┐
        │ a            ┆ padded       │
        │ ---          ┆ ---          │
        │ str          ┆ str          │
        ╞══════════════╪══════════════╡
        │ cow          ┆ *****cow     │
        │ monkey       ┆ **monkey     │
        │ hippopotamus ┆ hippopotamus │
        │ null         ┆ null         │
        └──────────────┴──────────────┘
        """
        length_pyexpr = parse_into_expression(length)
        if not isinstance(fill_char, str):
            msg = f'"pad_start" expects a `str`, given a {qualified_type_name(fill_char)!r}'
            raise TypeError(msg)
        return wrap_expr(self._pyexpr.str_pad_start(length_pyexpr, fill_char))

    def pad_end(self, length: int | IntoExprColumn, fill_char: str = " ") -> Expr:
        """
        Pad the end of the string until it reaches the given length.

        .. engine-support:: in-memory, streaming, distributed

        Parameters
        ----------
        length
            Pad the string until it reaches this length. Strings with length equal to or
            greater than this value are returned as-is. Can be int or expression.
        fill_char
            The character to pad the string with.

        See Also
        --------

View on GitHub (pinned to df599052da)

Solutions

  1. Pass fill_char as a str, e.g. fill_char='0' or fill_char='*'
  2. If the value comes from config or data, convert before the call: str(value) or chr(code)
  3. For zero-padding use .str.zfill(length), which takes no fill_char at all
  4. A per-row fill character is unsupported; preprocess with map_batches if you truly need it

Example fix

# before
pl.col('id').str.pad_start(8, fill_char=0)

# after
pl.col('id').str.pad_start(8, fill_char='0')
# or, for zeros specifically:
pl.col('id').str.zfill(8)
Defensive patterns

Strategy: type-guard

Validate before calling

fill_char = ' '
assert isinstance(fill_char, str), 'fill_char must be str'
expr = pl.col('s').str.pad_start(10, fill_char=fill_char)

Type guard

def is_valid_fill_char(x: object) -> bool:
    return isinstance(x, str) and len(x) >= 1

Prevention

When it happens

Trigger: Calling pl.col('s').str.pad_start(10, fill_char=0), fill_char=None (e.g. an unset config value), fill_char=b'*' (bytes from a network/serialization layer), or passing a column expression as fill_char (only length is an IntoExpr; fill_char is a literal str parameter).

Common situations: Zero-padding IDs and passing the integer 0 instead of the string '0'; bytes values leaking in from JSON/binary protocols; an optional settings dict yielding None; assuming every string-namespace parameter accepts expressions.

Related errors


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