{"record":{"id":"ba422490df2577b4","repo":"pola-rs/polars","slug":"bitmask-must-be-inverted","errorCode":null,"errorMessage":"bitmask must be inverted","messagePattern":"bitmask must be inverted","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":312,"sourceCode":"        msg = f\"unsupported null type: {null_type!r}\"\n        raise NotImplementedError(msg)\n\n\ndef _construct_validity_buffer_from_bitmask(\n    buffer: Buffer,\n    null_value: int,\n    length: int,\n    offset: int = 0,\n    *,\n    allow_copy: bool,\n) -> Series:\n    buffer_info = (buffer.ptr, offset, length)\n    s = pl.Series._from_buffer(Boolean, buffer_info, buffer)\n\n    if null_value != 0:\n        if not allow_copy:\n            msg = \"bitmask must be inverted\"\n            raise CopyNotAllowedError(msg)\n        s = ~s\n\n    return s\n\n\ndef _construct_validity_buffer_from_bytemask(\n    buffer: Buffer,\n    null_value: int,\n    *,\n    allow_copy: bool,\n) -> Series:\n    if not allow_copy:\n        msg = \"bytemask must be converted into a bitmask\"\n        raise CopyNotAllowedError(msg)\n\n    buffer_info = (buffer.ptr, 0, buffer.bufsize)\n    s = pl.Series._from_buffer(UInt8, buffer_info, owner=buffer)\n    s = s.cast(Boolean)","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/pola-rs/polars/blob/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/from_dataframe.py#L294-L330","documentation":"For USE_BITMAP validity buffers, Polars assumes the standard semantics bit=1 means valid, null_value=0. If the producer reports null_value != 0 (inverted bitmask, where set bits mark nulls), Polars must invert the mask with ~s — an allocation — so with allow_copy=False it raises CopyNotAllowedError('bitmask must be inverted').","triggerScenarios":"pl.from_dataframe(obj, allow_copy=False) where a column's describe_null() == (ColumnNullType.USE_BITMAP, 1) — producers that store null flags instead of validity flags.","commonSituations":"Interchange producers derived from numpy boolean masks (True = missing) that expose them as inverted bitmasks; zero-copy pipelines expecting standard Arrow-style validity bitmaps.","solutions":["Call pl.from_dataframe(obj, allow_copy=True) to permit the inversion.","Have the producer emit a standard validity bitmask with null_value 0.","Catch CopyNotAllowedError and retry with allow_copy=True."],"exampleFix":"# before\ndf = pl.from_dataframe(obj, allow_copy=False)  # inverted bitmask -> CopyNotAllowedError\n\n# after\ndf = pl.from_dataframe(obj, allow_copy=True)   # ~mask computed","handlingStrategy":"fallback","validationCode":"def has_inverted_bitmask(icdf) -> bool:\n    return any(\n        icdf.get_column(i).describe_null() == (ColumnNullType.USE_BITMAP, 1)\n        for i in range(icdf.num_columns())\n    )\n\nallow_copy = not has_inverted_bitmask(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 ~mask inversion","preventionTips":["Emit standard validity bitmaps (bit=1 valid, null_value=0) from producers.","Document inversion semantics explicitly if you must use inverted masks."],"tags":["polars","interchange-protocol","bitmask","validity","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"}