pola-rs/polars · error · CopyNotAllowedError
string buffers must be converted
Error message
string buffers must be converted
What it means
Raised by PolarsColumn.get_buffers in the interchange protocol when the column is a String (utf8) dtype and allow_copy=False. Polars' string view layout must be converted to the interchange spec's offsets representation, which requires copying, so the zero-copy contract cannot be honored for strings.
Source
Thrown at py-polars/src/polars/interchange/column.py:157
else:
subchunks_per_chunk = n_chunks // total_n_chunks
for chunk in chunks:
size = len(chunk)
step = size // subchunks_per_chunk
if size % subchunks_per_chunk != 0:
step += 1
for start in range(0, step * subchunks_per_chunk, step):
yield PolarsColumn(
chunk[start : start + step], allow_copy=self._allow_copy
)
def get_buffers(self) -> ColumnBuffers:
"""Return a dictionary containing the underlying buffers."""
dtype = self._col.dtype
if dtype == String and not self._allow_copy:
msg = "string buffers must be converted"
raise CopyNotAllowedError(msg)
buffers = self._col._get_buffers()
return {
"data": self._wrap_data_buffer(buffers["values"]),
"validity": self._wrap_validity_buffer(buffers["validity"]),
"offsets": self._wrap_offsets_buffer(buffers["offsets"]),
}
def _wrap_data_buffer(self, buffer: Series) -> tuple[PolarsBuffer, Dtype]:
interchange_buffer = PolarsBuffer(buffer, allow_copy=self._allow_copy)
dtype = polars_dtype_to_dtype(buffer.dtype)
return interchange_buffer, dtype
def _wrap_validity_buffer(
self, buffer: Series | None
) -> tuple[PolarsBuffer, Dtype] | None:
if buffer is None:View on GitHub (pinned to df599052da)
Solutions
- Pass allow_copy=True whenever the frame contains String columns
- Drop or transform string columns before interchange (e.g. cast to Categorical is not zero-copy either — better to exclude them from the strict path)
- If the consumer only needs values, read the interchange column's data via its own iteration APIs rather than raw buffers
Example fix
// before df.__dataframe__(allow_copy=False).column(0).get_buffers() # column 0 is String // after df.__dataframe__(allow_copy=True).column(0).get_buffers()
Defensive patterns
Strategy: validation
Validate before calling
string_cols = [name for name, dt in df.schema.items() if dt == pl.String] allow_copy = False if not string_cols else True
Type guard
def frame_is_zero_copy_safe(df) -> bool:
return not any(dt == pl.String for dt in df.schema.values()) Try / catch
from polars.interchange.protocol import CopyNotAllowedError
try:
buffers = col.get_buffers()
except CopyNotAllowedError:
buffers = PolarsColumn(col._col, allow_copy=True).get_buffers() Prevention
- Treat String columns as copy-requiring in the interchange protocol
- Split strict zero-copy paths to numeric-only frames
When it happens
Trigger: Calling __dataframe__(allow_copy=False) and then get_buffers() on a String column; strict zero-copy consumers (query compilers, arrow-free bridges) walking buffers of a frame that contains string columns; benchmark harnesses asserting no-copy interchange.
Common situations: Frames with string keys/labels passed to allow_copy=False consumers; migrating from older polars where string interchange had different copy behavior; performance contracts that forbid copies on hot paths containing text data.
Related errors
- string buffers must be converted
- offsets buffer must be cast from {polars_dtype} to Int64
- non-contiguous buffer must be made contiguous
- unevenly chunked columns must be rechunked
- cannot create String column without an offsets buffer
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/599032baca7e42d0.
Report an issue: GitHub.