{"record":{"id":"eeadce44158ec8a0","repo":"pola-rs/polars","slug":"prefix-must-be-a-string-found-qualified-type-n","errorCode":null,"errorMessage":"'prefix' must be a string; found {qualified_type_name(prefix)!r}","messagePattern":"'prefix' must be a string; found (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/expr/categorical.py","lineNumber":204,"sourceCode":"        │ mango  ┆ false      │\n        │ null   ┆ null       │\n        └────────┴────────────┘\n\n        Using `starts_with` as a filter condition:\n\n        >>> df.filter(pl.col(\"fruits\").cat.starts_with(\"app\"))\n        shape: (1, 1)\n        ┌────────┐\n        │ fruits │\n        │ ---    │\n        │ cat    │\n        ╞════════╡\n        │ apple  │\n        └────────┘\n        \"\"\"\n        if not isinstance(prefix, str):\n            msg = f\"'prefix' must be a string; found {qualified_type_name(prefix)!r}\"\n            raise TypeError(msg)\n        return wrap_expr(self._pyexpr.cat_starts_with(prefix))\n\n    def ends_with(self, suffix: str) -> Expr:\n        \"\"\"\n        Check if string representations of values end with a substring.\n\n        .. engine-support:: in-memory, streaming, distributed\n\n        Parameters\n        ----------\n        suffix\n            Suffix substring.\n\n        See Also\n        --------\n        contains : Check if string reprs contains a substring that matches a pattern.\n        starts_with : Check if string reprs start with a substring.\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/expr/categorical.py#L186-L222","documentation":"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.","triggerScenarios":"pl.col('c').cat.starts_with(b'app') (bytes from parquet/protobuf pipelines), .cat.starts_with(123), or .cat.starts_with(pl.lit('app')).","commonSituations":"Prefixes arriving as bytes from binary formats or network layers; passing expressions by habit from str-like APIs; numeric prefixes from config.","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"],"exampleFix":"# before\npl.col('cat_col').cat.starts_with(b'app')  # TypeError\n\n# after\npl.col('cat_col').cat.starts_with(b'app'.decode())\n# per-row dynamic: pl.col('cat_col').cast(pl.String).str.starts_with(pl.col('prefix_col'))","handlingStrategy":"type-guard","validationCode":"if isinstance(prefix, bytes):\n    prefix = prefix.decode()\nassert isinstance(prefix, str), 'cat.starts_with requires a str prefix'\nexpr = pl.col('c').cat.starts_with(prefix)","typeGuard":"from typing import TypeGuard\n\ndef is_str_prefix(p) -> TypeGuard[str]:\n    return isinstance(p, str)","tryCatchPattern":"try:\n    e = pl.col('c').cat.starts_with(prefix)\nexcept TypeError:\n    e = pl.col('c').cast(pl.String).str.starts_with(str(prefix))","preventionTips":["Decode bytes prefixes at ingestion boundaries","For per-row dynamic prefixes use cast(pl.String).str.starts_with(other_expr)"],"tags":["categorical","type-guard","string","prefix"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}