pola-rs/polars · error · CopyNotAllowedError

bitmask must be constructed

Error message

bitmask must be constructed

What it means

When a column declares ColumnNullType.USE_NAN, validity cannot be a zero-copy buffer view: Polars must compute data.is_not_nan(), which allocates a new boolean Series. Because allow_copy=False forbids that allocation, Polars raises CopyNotAllowedError('bitmask must be constructed').

Source

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

        if validity_buffer_info is None:
            return None
        buffer = validity_buffer_info[0]
        return _construct_validity_buffer_from_bitmask(
            buffer, null_value, column.size(), offset, allow_copy=allow_copy
        )

    elif null_type == ColumnNullType.USE_BYTEMASK:
        if validity_buffer_info is None:
            return None
        buffer = validity_buffer_info[0]
        return _construct_validity_buffer_from_bytemask(
            buffer, null_value, allow_copy=allow_copy
        )

    elif null_type == ColumnNullType.USE_NAN:
        if not allow_copy:
            msg = "bitmask must be constructed"
            raise CopyNotAllowedError(msg)
        return data.is_not_nan()

    elif null_type == ColumnNullType.USE_SENTINEL:
        if not allow_copy:
            msg = "bitmask must be constructed"
            raise CopyNotAllowedError(msg)

        sentinel = pl.Series([null_value])
        try:
            if column_dtype.is_temporal():
                sentinel = sentinel.cast(column_dtype)
            return data != sentinel  # noqa: TRY300
        except InvalidOperationError as e:
            msg = f"invalid sentinel value for column of type {column_dtype}: {null_value!r}"
            raise TypeError(msg) from e

    else:
        msg = f"unsupported null type: {null_type!r}"

View on GitHub (pinned to df599052da)

Solutions

  1. Retry with allow_copy=True
  2. Have the producer supply an explicit validity bitmask (USE_BITMASK) instead of NaN semantics
  3. Materialize validity upstream (replace NaN with real nulls in the producer) before conversion

Example fix

// before
df = pl.from_dataframe(pandas_like_df, allow_copy=False)  # NaN nulls -> error

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

Strategy: try-catch

Validate before calling

def columns_use_nan_nulls(df) -> bool:
    from polars.interchange.protocol import ColumnNullType
    proto = df.__dataframe__(allow_copy=False)
    return any(
        col.describe_null()[0] == ColumnNullType.USE_NAN
        for col in proto.get_columns()
    )

Try / catch

try:
    out = pl.from_dataframe(df, allow_copy=False)
except pl.exceptions.CopyNotAllowedError:
    # NaN-derived validity allocates; allow the copy
    out = pl.from_dataframe(df, allow_copy=True)

Prevention

When it happens

Trigger: allow_copy=False conversion of a float column whose producer declares NaN-based nulls (USE_NAN) in describe_null.

Common situations: pandas-like producers that represent missing float values as NaN instead of a validity mask; numeric zero-copy pipelines in ML feature serving.

Related errors


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