{"record":{"id":"269634cca801ae5e","repo":"pola-rs/polars","slug":"string-buffers-must-be-converted-269634","errorCode":null,"errorMessage":"string buffers must be converted","messagePattern":"string buffers must be converted","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":111,"sourceCode":") -> Series:\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    return pl.Series._from_buffers(dtype, data=data_buffer, validity=validity_buffer)\n\n\ndef _string_column_to_series(column: Column, *, allow_copy: bool) -> Series:\n    if column.size() == 0:\n        return pl.Series(dtype=String)\n    elif not allow_copy:\n        msg = \"string buffers must be converted\"\n        raise CopyNotAllowedError(msg)\n\n    buffers = column.get_buffers()\n    offset = column.offset\n\n    offsets_buffer_info = buffers[\"offsets\"]\n    if offsets_buffer_info is None:\n        msg = \"cannot create String column without an offsets buffer\"\n        raise RuntimeError(msg)\n    offsets_buffer = _construct_offsets_buffer(\n        *offsets_buffer_info, offset, allow_copy=allow_copy\n    )\n\n    buffer, dtype = buffers[\"data\"]\n    data_buffer = _construct_data_buffer(\n        buffer, dtype, buffer.bufsize, offset=0, allow_copy=allow_copy\n    )\n\n    # First construct a Series without a validity buffer","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/pola-rs/polars/blob/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/from_dataframe.py#L93-L129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ndf = pl.from_dataframe(obj, allow_copy=False)  # string column -> CopyNotAllowedError\n\n# after\ndf = pl.from_dataframe(obj, allow_copy=True)","handlingStrategy":"fallback","validationCode":"from polars.interchange.protocol import DtypeKind\n\ndef contains_strings(icdf) -> bool:\n    return any(\n        icdf.get_column(i).dtype[0] == DtypeKind.STRING and icdf.get_column(i).size() > 0\n        for i in range(icdf.num_columns())\n    )\n\nallow_copy = not contains_strings(obj.__dataframe__())\ndf = pl.from_dataframe(obj, allow_copy=allow_copy)","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)  # strings always require one copy","preventionTips":["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."],"tags":["polars","interchange-protocol","string","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"}