pola-rs/polars · error · CopyNotAllowedError

byte-packed boolean buffer must be converted to bit-packed…

Error message

byte-packed boolean buffer must be converted to bit-packed boolean

What it means

The interchange protocol allows boolean buffers to be bit-packed (1 bit per value) or byte-packed (8 bits per value, dtype[1] == 8). Polars represents booleans bit-packed, so a byte-packed buffer must be wrapped as UInt8 and cast to Boolean — a copy. With allow_copy=False and a non-empty buffer this raises CopyNotAllowedError; length-0 buffers return an empty Boolean Series.

Solutions

  1. Call pl.from_dataframe(obj, allow_copy=True) to permit the UInt8->Boolean cast.
  2. Have the producer bit-pack boolean columns before export.
  3. Catch CopyNotAllowedError and retry with allow_copy=True.

Example fix

# before
df = pl.from_dataframe(obj, allow_copy=False)  # byte-packed bool -> CopyNotAllowedError

# after
df = pl.from_dataframe(obj, allow_copy=True)
Defensive patterns

Strategy: fallback

Validate before calling

def has_byte_packed_bools(icdf) -> bool:
    return any(
        (c := icdf.get_column(i)).dtype[:2] == (DtypeKind.BOOL, 8) and c.size() > 0
        for i in range(icdf.num_columns())
    )

allow_copy = not has_byte_packed_bools(obj.__dataframe__())

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)  # allows UInt8 -> Boolean cast

Prevention

When it happens

Trigger: pl.from_dataframe(obj, allow_copy=False) where a boolean column's dtype tuple has bit width 8 — the natural layout for numpy bool masks and several interchange producers.

Common situations: Importing from producers backed by numpy bool arrays; zero-copy pipelines that also carry flag/mask columns; mixed-width boolean handling in custom interop code.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/1ae18d18fa74ab44. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/interchange/from_dataframe.py:215

    *,
    allow_copy: bool,
) -> Series:
    polars_dtype = dtype_to_polars_dtype(dtype)

    # Handle implementations that incorrectly set the data buffer dtype
    # to the column dtype
    # https://github.com/pola-rs/polars/pull/10787
    polars_dtype = polars_dtype_to_data_buffer_dtype(polars_dtype)

    buffer_info = (buffer.ptr, offset, length)

    # Handle byte-packed boolean buffer
    if polars_dtype == Boolean and dtype[1] == 8:
        if length == 0:
            return pl.Series(dtype=Boolean)
        elif not allow_copy:
            msg = "byte-packed boolean buffer must be converted to bit-packed boolean"
            raise CopyNotAllowedError(msg)
        return pl.Series._from_buffer(UInt8, buffer_info, owner=buffer).cast(Boolean)

    return pl.Series._from_buffer(polars_dtype, buffer_info, owner=buffer)


def _construct_offsets_buffer(
    buffer: Buffer,
    dtype: Dtype,
    offset: int,
    *,
    allow_copy: bool,
) -> Series:
    polars_dtype = dtype_to_polars_dtype(dtype)
    length = get_buffer_length_in_elements(buffer.bufsize, dtype) - offset

    buffer_info = (buffer.ptr, offset, length)
    s = pl.Series._from_buffer(polars_dtype, buffer_info, owner=buffer)

View on GitHub (pinned to 9b5d73fd00)