pola-rs/polars · error · CopyNotAllowedError
string buffers must be converted
Error message
string buffers must be converted
What it means
Ingesting a string column through the interchange protocol always materializes new data+offsets buffers into a Polars String Series — it is never zero-copy — so _string_column_to_series raises CopyNotAllowedError when allow_copy=False. Only empty string columns (size 0) are exempt. Dictionary-encoded columns with string categories hit this too, because categories are converted via the same path.
Solutions
- Call pl.from_dataframe(obj, allow_copy=True) (or drop the argument — the default allows copies).
- If zero-copy is a hard requirement, exclude or re-encode string columns on the producer side before conversion.
- Catch CopyNotAllowedError and retry with allow_copy=True only for the frames that need it.
Example fix
# before df = pl.from_dataframe(obj, allow_copy=False) # string column -> CopyNotAllowedError # after df = pl.from_dataframe(obj, allow_copy=True)
Defensive patterns
Strategy: fallback
Validate before calling
from polars.interchange.protocol import DtypeKind
def contains_strings(icdf) -> bool:
return any(
icdf.get_column(i).dtype[0] == DtypeKind.STRING and icdf.get_column(i).size() > 0
for i in range(icdf.num_columns())
)
allow_copy = not contains_strings(obj.__dataframe__())
df = pl.from_dataframe(obj, allow_copy=allow_copy) Try / catch
from polars.interchange.protocol import CopyNotAllowedError
try:
df = pl.from_dataframe(obj, allow_copy=False)
except CopyNotAllowedError:
df = pl.from_dataframe(obj, allow_copy=True) # strings always require one copy Prevention
- Treat allow_copy=False as an assertion, not an optimization — retry with True on CopyNotAllowedError.
- Profile which dtypes your producers emit; string and categorical columns are never zero-copy in this path.
- For copy-sensitive text pipelines, transfer via Arrow (pl.from_arrow) where string copies are cheaper.
When it happens
Trigger: pl.from_dataframe(obj, allow_copy=False) where obj contains at least one non-empty string column (DtypeKind.STRING), or a dictionary/categorical column whose categories are strings; also reached via _categorical_column_to_series for the categories buffer.
Common situations: Zero-copy ingestion pipelines that pass allow_copy=False to enforce no allocation, then hit text columns; ETL from pandas/pyarrow-backed producers with object/string data; assuming string data can be imported without copying.
Related errors
- offsets buffer must be cast from
- bitmask must be constructed
- bitmask must be inverted
- byte-packed boolean buffer must be converted to bit-packed…
- bytemask must be converted into a bitmask
AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19).
Data as JSON: /api/errors/269634cca801ae5e.
Report an issue: GitHub.
Appendix: source
Thrown at py-polars/src/polars/interchange/from_dataframe.py:111
) -> Series:
buffers = column.get_buffers()
offset = column.offset
data_buffer = _construct_data_buffer(
*buffers["data"], column.size(), offset, allow_copy=allow_copy
)
validity_buffer = _construct_validity_buffer(
buffers["validity"], column, dtype, data_buffer, offset, allow_copy=allow_copy
)
return pl.Series._from_buffers(dtype, data=data_buffer, validity=validity_buffer)
def _string_column_to_series(column: Column, *, allow_copy: bool) -> Series:
if column.size() == 0:
return pl.Series(dtype=String)
elif not allow_copy:
msg = "string buffers must be converted"
raise CopyNotAllowedError(msg)
buffers = column.get_buffers()
offset = column.offset
offsets_buffer_info = buffers["offsets"]
if offsets_buffer_info is None:
msg = "cannot create String column without an offsets buffer"
raise RuntimeError(msg)
offsets_buffer = _construct_offsets_buffer(
*offsets_buffer_info, offset, allow_copy=allow_copy
)
buffer, dtype = buffers["data"]
data_buffer = _construct_data_buffer(
buffer, dtype, buffer.bufsize, offset=0, allow_copy=allow_copy
)
# First construct a Series without a validity bufferView on GitHub (pinned to 9b5d73fd00)