pola-rs/polars · error · CopyNotAllowedError

bitmask must be inverted

Error message

bitmask must be inverted

What it means

For bitmask validity, Polars reads the buffer as a Boolean Series and, when the producer reports null_value != 0 (bits mark nulls instead of valids), must invert it with ~s to obtain a validity mask. The inversion allocates, so under allow_copy=False Polars raises CopyNotAllowedError('bitmask must be inverted').

Source

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

        msg = f"unsupported null type: {null_type!r}"
        raise NotImplementedError(msg)


def _construct_validity_buffer_from_bitmask(
    buffer: Buffer,
    null_value: int,
    length: int,
    offset: int = 0,
    *,
    allow_copy: bool,
) -> Series:
    buffer_info = (buffer.ptr, offset, length)
    s = pl.Series._from_buffer(Boolean, buffer_info, buffer)

    if null_value != 0:
        if not allow_copy:
            msg = "bitmask must be inverted"
            raise CopyNotAllowedError(msg)
        s = ~s

    return s


def _construct_validity_buffer_from_bytemask(
    buffer: Buffer,
    null_value: int,
    *,
    allow_copy: bool,
) -> Series:
    if not allow_copy:
        msg = "bytemask must be converted into a bitmask"
        raise CopyNotAllowedError(msg)

    buffer_info = (buffer.ptr, 0, buffer.bufsize)
    s = pl.Series._from_buffer(UInt8, buffer_info, owner=buffer)
    s = s.cast(Boolean)

View on GitHub (pinned to df599052da)

Solutions

  1. Retry with allow_copy=True
  2. Have the producer emit a standard validity mask with null_value=0 (bit set = valid)
  3. Invert the mask upstream in the producer

Example fix

// before
df = pl.from_dataframe(producer_df, allow_copy=False)  # inverted bitmask -> error

// after
try:
    df = pl.from_dataframe(producer_df, allow_copy=False)
except pl.exceptions.CopyNotAllowedError:
    df = pl.from_dataframe(producer_df, allow_copy=True)
Defensive patterns

Strategy: try-catch

Validate before calling

from polars.interchange.protocol import ColumnNullType

def bitmasks_need_inversion(df) -> bool:
    proto = df.__dataframe__(allow_copy=False)
    return any(
        col.describe_null()[:2] == (ColumnNullType.USE_BITMASK, 1)
        for col in proto.get_columns()
    )

Try / catch

try:
    out = pl.from_dataframe(df, allow_copy=False)
except pl.exceptions.CopyNotAllowedError:
    # inverted bitmask must be flipped; allow the copy
    out = pl.from_dataframe(df, allow_copy=True)

Prevention

When it happens

Trigger: allow_copy=False conversion of a column with USE_BITMASK validity where describe_null reports null_value=1 (null-indicator mask instead of validity mask).

Common situations: Producers that store null-indicator masks (1 = null) rather than validity masks (1 = valid); zero-copy consumers that assume bitmask validity is always free.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/ba422490df2577b4. Report an issue: GitHub.