{"record":{"id":"470b61b5b43d1df8","repo":"pola-rs/polars","slug":"bitmask-must-be-constructed","errorCode":null,"errorMessage":"bitmask must be constructed","messagePattern":"bitmask must be constructed","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":276,"sourceCode":"        if validity_buffer_info is None:\n            return None\n        buffer = validity_buffer_info[0]\n        return _construct_validity_buffer_from_bitmask(\n            buffer, null_value, column.size(), offset, allow_copy=allow_copy\n        )\n\n    elif null_type == ColumnNullType.USE_BYTEMASK:\n        if validity_buffer_info is None:\n            return None\n        buffer = validity_buffer_info[0]\n        return _construct_validity_buffer_from_bytemask(\n            buffer, null_value, allow_copy=allow_copy\n        )\n\n    elif null_type == ColumnNullType.USE_NAN:\n        if not allow_copy:\n            msg = \"bitmask must be constructed\"\n            raise CopyNotAllowedError(msg)\n        return data.is_not_nan()\n\n    elif null_type == ColumnNullType.USE_SENTINEL:\n        if not allow_copy:\n            msg = \"bitmask must be constructed\"\n            raise CopyNotAllowedError(msg)\n\n        sentinel = pl.Series([null_value])\n        try:\n            if column_dtype.is_temporal():\n                sentinel = sentinel.cast(column_dtype)\n            return data != sentinel  # noqa: TRY300\n        except InvalidOperationError as e:\n            msg = f\"invalid sentinel value for column of type {column_dtype}: {null_value!r}\"\n            raise TypeError(msg) from e\n\n    else:\n        msg = f\"unsupported null type: {null_type!r}\"","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/interchange/from_dataframe.py#L258-L294","documentation":"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').","triggerScenarios":"allow_copy=False conversion of a float column whose producer declares NaN-based nulls (USE_NAN) in describe_null.","commonSituations":"pandas-like producers that represent missing float values as NaN instead of a validity mask; numeric zero-copy pipelines in ML feature serving.","solutions":["Retry with allow_copy=True","Have the producer supply an explicit validity bitmask (USE_BITMASK) instead of NaN semantics","Materialize validity upstream (replace NaN with real nulls in the producer) before conversion"],"exampleFix":"// before\ndf = pl.from_dataframe(pandas_like_df, allow_copy=False)  # NaN nulls -> error\n\n// after\ntry:\n    df = pl.from_dataframe(pandas_like_df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    df = pl.from_dataframe(pandas_like_df, allow_copy=True)","handlingStrategy":"try-catch","validationCode":"def columns_use_nan_nulls(df) -> bool:\n    from polars.interchange.protocol import ColumnNullType\n    proto = df.__dataframe__(allow_copy=False)\n    return any(\n        col.describe_null()[0] == ColumnNullType.USE_NAN\n        for col in proto.get_columns()\n    )","typeGuard":null,"tryCatchPattern":"try:\n    out = pl.from_dataframe(df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    # NaN-derived validity allocates; allow the copy\n    out = pl.from_dataframe(df, allow_copy=True)","preventionTips":["Producers: prefer explicit validity bitmasks over NaN semantics for interchange consumers","Check describe_null() of each column before promising zero-copy behavior","Keep a single retry-with-copy helper for all CopyNotAllowedError cases in your ingest code"],"tags":["polars","interchange-protocol","null-handling","nan","zero-copy"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}