pola-rs/polars · error · TypeError
'prefix' must be a string; found {qualified_type_name(prefix
Error message
'prefix' must be a string; found {qualified_type_name(prefix)!r} What it means
Expr.cat.starts_with checks categorical values against a literal string prefix and requires the prefix to be a Python str. Any non-str value (bytes, int, pl.Expr) raises TypeError naming the actual type. There is no expression overload here — the prefix must be a concrete string.
Source
Thrown at py-polars/src/polars/expr/categorical.py:204
│ mango ┆ false │
│ null ┆ null │
└────────┴────────────┘
Using `starts_with` as a filter condition:
>>> df.filter(pl.col("fruits").cat.starts_with("app"))
shape: (1, 1)
┌────────┐
│ fruits │
│ --- │
│ cat │
╞════════╡
│ apple │
└────────┘
"""
if not isinstance(prefix, str):
msg = f"'prefix' must be a string; found {qualified_type_name(prefix)!r}"
raise TypeError(msg)
return wrap_expr(self._pyexpr.cat_starts_with(prefix))
def ends_with(self, suffix: str) -> Expr:
"""
Check if string representations of values end with a substring.
.. engine-support:: in-memory, streaming, distributed
Parameters
----------
suffix
Suffix substring.
See Also
--------
contains : Check if string reprs contains a substring that matches a pattern.
starts_with : Check if string reprs start with a substring.
View on GitHub (pinned to df599052da)
Solutions
- Decode bytes to str first: prefix.decode() if isinstance(prefix, bytes) else prefix
- Coerce at the boundary: .cat.starts_with(str(prefix))
- If you need per-row dynamic prefixes, cast the column to String and use .str.starts_with(other_expr) instead
Example fix
# before
pl.col('cat_col').cat.starts_with(b'app') # TypeError
# after
pl.col('cat_col').cat.starts_with(b'app'.decode())
# per-row dynamic: pl.col('cat_col').cast(pl.String).str.starts_with(pl.col('prefix_col')) Defensive patterns
Strategy: type-guard
Validate before calling
if isinstance(prefix, bytes):
prefix = prefix.decode()
assert isinstance(prefix, str), 'cat.starts_with requires a str prefix'
expr = pl.col('c').cat.starts_with(prefix) Type guard
from typing import TypeGuard
def is_str_prefix(p) -> TypeGuard[str]:
return isinstance(p, str) Try / catch
try:
e = pl.col('c').cat.starts_with(prefix)
except TypeError:
e = pl.col('c').cast(pl.String).str.starts_with(str(prefix)) Prevention
- Decode bytes prefixes at ingestion boundaries
- For per-row dynamic prefixes use cast(pl.String).str.starts_with(other_expr)
When it happens
Trigger: pl.col('c').cat.starts_with(b'app') (bytes from parquet/protobuf pipelines), .cat.starts_with(123), or .cat.starts_with(pl.lit('app')).
Common situations: Prefixes arriving as bytes from binary formats or network layers; passing expressions by habit from str-like APIs; numeric prefixes from config.
Related errors
- 'suffix' must be a string; found {qualified_type_name(suffix
- unexpected column selection {col_selection!r}
- cannot use `__setitem__` on DataFrame with key {key!r} of ty
- expected 'time' to be a Python time or Polars expression, fo
- 'offset' must be an integer, string, or expression, not {typ
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/eeadce44158ec8a0.
Report an issue: GitHub.