{"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/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/from_dataframe.py#L258-L294","documentation":"When a column declares USE_NAN nullability (nulls represented by NaN, typical for floats), Polars must compute a validity mask via data.is_not_nan() — a new allocation, not zero-copy. With allow_copy=False this raises CopyNotAllowedError('bitmask must be constructed').","triggerScenarios":"pl.from_dataframe(obj, allow_copy=False) where describe_null()[0] == ColumnNullType.USE_NAN for some column — producers that conflate NaN and null, or were asked for nan_as_null semantics.","commonSituations":"Ingesting float columns from producers that mark missing values as NaN; zero-copy pipelines that expect bitmask validity but receive NaN-based validity.","solutions":["Call pl.from_dataframe(obj, allow_copy=True) so the validity mask can be computed.","Have the producer export a proper bitmask (USE_BITMAP) validity buffer instead of USE_NAN.","Catch CopyNotAllowedError and retry with allow_copy=True."],"exampleFix":"# before\ndf = pl.from_dataframe(obj, allow_copy=False)  # USE_NAN column -> CopyNotAllowedError\n\n# after\ndf = pl.from_dataframe(obj, allow_copy=True)   # builds data.is_not_nan() mask","handlingStrategy":"fallback","validationCode":"def uses_nan_nulls(icdf) -> bool:\n    return any(\n        icdf.get_column(i).describe_null()[0] == ColumnNullType.USE_NAN\n        for i in range(icdf.num_columns())\n    )\n\nallow_copy = not uses_nan_nulls(obj.__dataframe__())","typeGuard":null,"tryCatchPattern":"from polars.interchange.protocol import CopyNotAllowedError\n\ntry:\n    df = pl.from_dataframe(obj, allow_copy=False)\nexcept CopyNotAllowedError:\n    df = pl.from_dataframe(obj, allow_copy=True)  # allows data.is_not_nan() mask","preventionTips":["Prefer bitmask validity in producers over NaN-as-null semantics.","Treat any non-bitmask null representation as copy-requiring when budgeting allocations."],"tags":["polars","interchange-protocol","validity","nan","allow-copy","copynotallowederror"],"backgroundTag":"copy-not-allowed","analyzedSha":"9b5d73fd00236295624374b075d16b1fe6ec6df9","analyzedAt":"2026-08-19T12:15:06.350Z","contentChangedAt":"2026-08-19T12:15:06.350Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}