{"record":{"id":"6739b7d17c2b1708","repo":"pola-rs/polars","slug":"data-buffer-must-be-cast-from-data-dtype-to-uint","errorCode":null,"errorMessage":"data buffer must be cast from {data_dtype} to UInt32","messagePattern":"data buffer must be cast from (.+?) to UInt32","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":183,"sourceCode":"    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(\n        data_dtype, data=data_buffer, validity=validity_buffer\n    )\n\n    # Polars only supports UInt32 categoricals\n    if data_dtype != UInt32:\n        if not allow_copy and column.size() > 0:\n            msg = f\"data buffer must be cast from {data_dtype} to UInt32\"\n            raise CopyNotAllowedError(msg)\n\n        # TODO: Cast directly to Enum\n        # https://github.com/pola-rs/polars/issues/13409\n        out = out.cast(UInt32)\n\n    return out.cast(dtype)\n\n\ndef _construct_data_buffer(\n    buffer: Buffer,\n    dtype: Dtype,\n    length: int,\n    offset: int = 0,\n    *,\n    allow_copy: bool,\n) -> Series:\n    polars_dtype = dtype_to_polars_dtype(dtype)\n","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/pola-rs/polars/blob/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/from_dataframe.py#L165-L201","documentation":"Polars stores categorical/Enum indices as UInt32. When an imported dictionary column's indices buffer has another dtype (UInt8/UInt16/Int32/Int64 — pyarrow commonly uses int8/int32 indices, pandas codes are int8..int32), Polars must cast the physical Series to UInt32, which allocates; with allow_copy=False and a non-empty column this raises CopyNotAllowedError. The Series is first built in the original dtype so out-of-range sentinels survive, then cast.","triggerScenarios":"pl.from_dataframe(obj, allow_copy=False) where obj contains a non-empty dictionary/categorical column whose indices buffer is not uint32 — the default layout of most Arrow-based producers.","commonSituations":"Zero-copy import requirements colliding with Arrow's variable-width index types; dictionary columns with low cardinality (int8 indices) coming from parquet/IPC files read by pyarrow.","solutions":["Call pl.from_dataframe(obj, allow_copy=True) so the indices can be cast to UInt32.","Re-encode the producer's dictionary to uint32 indices before export.","Catch CopyNotAllowedError and retry with allow_copy=True."],"exampleFix":"# before\ndf = pl.from_dataframe(obj, allow_copy=False)  # int8 indices -> CopyNotAllowedError\n\n# after\ndf = pl.from_dataframe(obj, allow_copy=True)   # indices cast to UInt32","handlingStrategy":"fallback","validationCode":"def needs_index_cast(icdf) -> bool:\n    for i in range(icdf.num_columns()):\n        col = icdf.get_column(i)\n        if col.dtype[0] != DtypeKind.CATEGORICAL:\n            continue\n        if not col.describe_categorical().get(\"is_dictionary\"):\n            continue\n        _, idx_dtype = col.get_buffers()[\"data\"]\n        if col.size() > 0 and (idx_dtype[0], idx_dtype[1]) != (DtypeKind.UINT, 32):\n            return True\n    return False\n\nallow_copy = not needs_index_cast(obj.__dataframe__())","typeGuard":null,"tryCatchPattern":"from polars.interchange.protocol import CopyNotAllowedError\n\ntry:\n    df = pl.from_dataframe(obj, allow_copy=False)\nexcept CopyNotAllowedError:\n    df = pl.from_dataframe(obj, allow_copy=True)  # allows the UInt32 index cast","preventionTips":["Expect non-uint32 dictionary indices from Arrow/pandas producers — they are the norm.","Only demand allow_copy=False for frames you have verified are zero-copy compatible.","Catch CopyNotAllowedError once at the pipeline boundary and relax the flag."],"tags":["polars","interchange-protocol","categorical","allow-copy","copynotallowederror"],"backgroundTag":"copy-not-allowed","analyzedSha":"9b5d73fd00236295624374b075d16b1fe6ec6df9","analyzedAt":"2026-08-19T12:15:06.350Z","contentChangedAt":"2026-08-19T12:15:06.350Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}