{"record":{"id":"e059529f9f26b9fc","repo":"pola-rs/polars","slug":"non-string-categories-are-not-supported","errorCode":null,"errorMessage":"non-string categories are not supported","messagePattern":"non-string categories are not supported","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":157,"sourceCode":"        data = pl.Series._from_buffers(\n            String, data=data_buffers, validity=validity_buffer\n        )\n\n    return data\n\n\ndef _categorical_column_to_series(column: Column, *, allow_copy: bool) -> Series:\n    categorical = column.describe_categorical\n    if not categorical[\"is_dictionary\"]:\n        msg = \"non-dictionary categoricals are not yet supported\"\n        raise NotImplementedError(msg)\n\n    categories_col = categorical[\"categories\"]\n    if categories_col.size() == 0:\n        dtype = Enum([])\n    elif categories_col.dtype[0] != DtypeKind.STRING:\n        msg = \"non-string categories are not supported\"\n        raise NotImplementedError(msg)\n    else:\n        categories = _string_column_to_series(categories_col, allow_copy=allow_copy)\n        dtype = Enum(categories)\n\n    buffers = column.get_buffers()\n    offset = column.offset\n\n    data_buffer = _construct_data_buffer(\n        *buffers[\"data\"], column.size(), offset, allow_copy=allow_copy\n    )\n    validity_buffer = _construct_validity_buffer(\n        buffers[\"validity\"], column, dtype, data_buffer, offset, allow_copy=allow_copy\n    )\n\n    # First construct a physical Series without categories\n    # to allow for sentinel values that do not fit in UInt32\n    data_dtype = data_buffer.dtype\n    out = pl.Series._from_buffers(","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/interchange/from_dataframe.py#L139-L175","documentation":"The categories of a dictionary-encoded categorical column must be strings. When the categories column's dtype kind is not DtypeKind.STRING (for example integer category codes), Polars cannot build the Enum type it uses for interchange categoricals and raises NotImplementedError. An empty categories column is accepted and maps to Enum([]).","triggerScenarios":"Dictionary-encoded column whose describe_categorical()['categories'].dtype[0] is not DtypeKind.STRING (e.g. INT category keys from the producer).","commonSituations":"Producers that store label-encoded vocabularies with integer keys (ML feature stores, database ENUM types with numeric codes); mapping legacy categorical encodings into polars.","solutions":["Make the producer expose string categories (cast integer codes to their string labels at the source)","Import the column as plain integers and apply the mapping in Polars afterwards with cast to Enum(['a','b',...]) using the known category list"],"exampleFix":"// before\n# producer exposes integer category codes -> NotImplementedError\npl.from_dataframe(producer_df)\n\n// after\n# import codes as integers, map to Enum in polars\ndf = pl.from_dataframe(producer_df_without_categorical)\ndf = df.with_columns(pl.col('code').cast(pl.Enum(['foo', 'bar', 'baz'])))","handlingStrategy":"validation","validationCode":"def categories_are_strings(df) -> bool:\n    from polars.interchange.protocol import DtypeKind\n    proto = df.__dataframe__(allow_copy=False)\n    for col in proto.get_columns():\n        if col.dtype[0] == DtypeKind.CATEGORICAL:\n            cats = col.describe_categorical()['categories']\n            if cats.size() > 0 and cats.dtype[0] != DtypeKind.STRING:\n                return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    out = pl.from_dataframe(df)\nexcept NotImplementedError as e:\n    if 'non-string categories' in str(e):\n        out = pl.from_dataframe(df_without_categorical_col)  # import raw codes\n        out = out.with_columns(pl.col('code').cast(known_enum))\n    else:\n        raise","preventionTips":["Expose string labels, not integer codes, as categories in producers","Keep the code-to-label mapping on the consumer side when producers can only emit integer codes","Test categorical round-trips with integer-keyed vocabularies explicitly"],"tags":["polars","interchange-protocol","categorical","enum","not-implemented"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}