{"record":{"id":"c8e5618bc04c04cc","repo":"pola-rs/polars","slug":"bytemask-must-be-converted-into-a-bitmask","errorCode":null,"errorMessage":"bytemask must be converted into a bitmask","messagePattern":"bytemask must be converted into a bitmask","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":326,"sourceCode":"\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)\n\n    if null_value != 0:\n        s = ~s\n\n    return s\n","sourceCodeStart":308,"sourceCodeEnd":336,"githubUrl":"https://github.com/pola-rs/polars/blob/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/from_dataframe.py#L308-L336","documentation":"USE_BYTEMASK validity (one byte per row) must always be converted into Polars' bit-packed boolean validity — there is no zero-copy path. _construct_validity_buffer_from_bytemask checks allow_copy before even reading the buffer and raises CopyNotAllowedError('bytemask must be converted into a bitmask') when copying is disallowed; an additional inversion applies when null_value != 0.","triggerScenarios":"pl.from_dataframe(obj, allow_copy=False) where describe_null()[0] == ColumnNullType.USE_BYTEMASK — the natural encoding when a producer's validity mask is a numpy uint8/bool array.","commonSituations":"Custom or numpy-backed interchange producers exporting per-byte masks; zero-copy pipelines that hit any bytemask column and fail immediately.","solutions":["Call pl.from_dataframe(obj, allow_copy=True) to permit the bytemask-to-bitmask conversion.","Have the producer bit-pack validity into a USE_BITMAP buffer instead.","Catch CopyNotAllowedError and retry with allow_copy=True."],"exampleFix":"# before\ndf = pl.from_dataframe(obj, allow_copy=False)  # bytemask column -> CopyNotAllowedError\n\n# after\ndf = pl.from_dataframe(obj, allow_copy=True)   # bytemask converted to bitmask","handlingStrategy":"fallback","validationCode":"def uses_bytemask(icdf) -> bool:\n    return any(\n        icdf.get_column(i).describe_null()[0] == ColumnNullType.USE_BYTEMASK\n        for i in range(icdf.num_columns())\n    )\n\nallow_copy = not uses_bytemask(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 bytemask -> bitmask conversion","preventionTips":["Bytemask validity can never be zero-copy with Polars — bit-pack it on the producer.","Introspect describe_null() before promising zero-copy conversion to callers."],"tags":["polars","interchange-protocol","bytemask","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"}