{"record":{"id":"a53d9de345e13b4f","repo":"pola-rs/polars","slug":"suffix-must-be-a-string-found-qualified-type-n","errorCode":null,"errorMessage":"'suffix' must be a string; found {qualified_type_name(suffix)!r}","messagePattern":"'suffix' must be a string; found (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/expr/categorical.py","lineNumber":231,"sourceCode":"        │ mango  ┆ true       │\n        │ null   ┆ null       │\n        └────────┴────────────┘\n\n        Using `ends_with` as a filter condition:\n\n        >>> df.filter(pl.col(\"fruits\").cat.ends_with(\"go\"))\n        shape: (1, 1)\n        ┌────────┐\n        │ fruits │\n        │ ---    │\n        │ cat    │\n        ╞════════╡\n        │ mango  │\n        └────────┘\n        \"\"\"\n        if not isinstance(suffix, str):\n            msg = f\"'suffix' must be a string; found {qualified_type_name(suffix)!r}\"\n            raise TypeError(msg)\n        return wrap_expr(self._pyexpr.cat_ends_with(suffix))\n\n    def slice(self, offset: int, length: int | None = None) -> Expr:\n        \"\"\"\n        Extract a substring from the string representation of each value.\n\n        .. engine-support:: in-memory, streaming, distributed\n\n        Parameters\n        ----------\n        offset\n            Start index. Negative indexing is supported.\n        length\n            Length of the slice. If set to `None` (default), the slice is taken to the\n            end of the string.\n\n        Returns\n        -------","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/py-polars/src/polars/expr/categorical.py#L213-L249","documentation":"Type-guard on the `suffix` parameter of the categorical ends_with method. The Python layer validates the argument before handing it to the Rust cat_ends_with implementation; passing anything other than a str (e.g. None, bytes, or a column-like object) triggers this TypeError naming the actual type received.","triggerScenarios":"Calling pl.col('c').cat.ends_with(42), ends_with(b'ngo'), ends_with(None), or passing an int/enum value instead of the string suffix.","commonSituations":"Suffix stored as a number in config (e.g. version suffix 1 instead of '1'); bytes from decoded data; variable mix-ups in generated expressions.","solutions":["Wrap the value with str(...) before calling","Decode bytes to str if the suffix comes from binary input","Guard with isinstance(suffix, str) and raise a clear error of your own","Confirm the variable passed actually holds the suffix string"],"exampleFix":"// before\ncat_col.cat.ends_with(version)  # version = 2 (int)\n// after\ncat_col.cat.ends_with(str(version))","handlingStrategy":"type-guard","validationCode":"if not isinstance(suffix, str):\n    raise TypeError(f'suffix must be str, got {type(suffix).__name__}')","typeGuard":"def is_str(x) -> bool:\n    return isinstance(x, str)","tryCatchPattern":"try:\n    e = col.cat.ends_with(suffix)\nexcept TypeError as e:\n    if 'must be a string' in str(e):\n        e = col.cat.ends_with(suffix.decode() if isinstance(suffix, bytes) else str(suffix))\n    else:\n        raise","preventionTips":["Coerce enum/numeric suffix values with str() before use","Validate config-derived suffixes are strings at load time","Avoid passing bytes; decode first"],"tags":["python","polars","type-error","categorical","strings"],"backgroundTag":"invalid-argument-value","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}