{"record":{"id":"1ae18d18fa74ab44","repo":"pola-rs/polars","slug":"byte-packed-boolean-buffer-must-be-converted-to-bi","errorCode":null,"errorMessage":"byte-packed boolean buffer must be converted to bit-packed boolean","messagePattern":"byte-packed boolean buffer must be converted to bit-packed boolean","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":215,"sourceCode":"    *,\n    allow_copy: bool,\n) -> Series:\n    polars_dtype = dtype_to_polars_dtype(dtype)\n\n    # Handle implementations that incorrectly set the data buffer dtype\n    # to the column dtype\n    # https://github.com/pola-rs/polars/pull/10787\n    polars_dtype = polars_dtype_to_data_buffer_dtype(polars_dtype)\n\n    buffer_info = (buffer.ptr, offset, length)\n\n    # Handle byte-packed boolean buffer\n    if polars_dtype == Boolean and dtype[1] == 8:\n        if length == 0:\n            return pl.Series(dtype=Boolean)\n        elif not allow_copy:\n            msg = \"byte-packed boolean buffer must be converted to bit-packed boolean\"\n            raise CopyNotAllowedError(msg)\n        return pl.Series._from_buffer(UInt8, buffer_info, owner=buffer).cast(Boolean)\n\n    return pl.Series._from_buffer(polars_dtype, buffer_info, owner=buffer)\n\n\ndef _construct_offsets_buffer(\n    buffer: Buffer,\n    dtype: Dtype,\n    offset: int,\n    *,\n    allow_copy: bool,\n) -> Series:\n    polars_dtype = dtype_to_polars_dtype(dtype)\n    length = get_buffer_length_in_elements(buffer.bufsize, dtype) - offset\n\n    buffer_info = (buffer.ptr, offset, length)\n    s = pl.Series._from_buffer(polars_dtype, buffer_info, owner=buffer)\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/interchange/from_dataframe.py#L197-L233","documentation":"The interchange protocol allows booleans packed one byte per value (bit width 8) or one bit per value (bit width 1). Polars stores booleans bit-packed, so a byte-packed buffer must be read as UInt8 and cast to Boolean, which allocates. With allow_copy=False and a non-empty column, Polars raises CopyNotAllowedError before doing the cast.","triggerScenarios":"allow_copy=False conversion of a non-empty Boolean column whose protocol dtype tuple has bit_width 8 (dtype[1] == 8).","commonSituations":"Simple or custom producers that find byte-per-boolean buffers easier to emit; strict zero-copy pipelines in memory-constrained services.","solutions":["Retry the conversion with allow_copy=True","Have the producer emit bit-packed (bit_width 1) boolean buffers","If the type change is acceptable, represent the column as UInt8 upstream to stay zero-copy"],"exampleFix":"// before\ndf = pl.from_dataframe(producer_df, allow_copy=False)  # byte-packed bools -> error\n\n// after\ntry:\n    df = pl.from_dataframe(producer_df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    df = pl.from_dataframe(producer_df, allow_copy=True)","handlingStrategy":"try-catch","validationCode":"def booleans_are_bit_packed(df) -> bool:\n    proto = df.__dataframe__(allow_copy=False)\n    for col in proto.get_columns():\n        if col.dtype[0] == 20 and col.dtype[1] == 8 and col.size() > 0:  # BOOL, 8 bits\n            return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    out = pl.from_dataframe(df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    # byte-packed bools must be cast to bit-packed; allow the copy\n    out = pl.from_dataframe(df, allow_copy=True)","preventionTips":["Producers: emit bit-packed booleans (bit_width 1) for zero-copy friendliness","Treat allow_copy=False as best-effort and keep a CopyNotAllowedError retry path","Document which producers emit byte-packed booleans so teams expect the copy"],"tags":["polars","interchange-protocol","boolean","zero-copy","memory"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}