{"record":{"id":"44ad7ca63e9cf750","repo":"pola-rs/polars","slug":"offsets-buffer-must-be-cast-from-polars-dtype-to","errorCode":null,"errorMessage":"offsets buffer must be cast from {polars_dtype} to Int64","messagePattern":"offsets buffer must be cast from (.+?) to Int64","errorType":"exception","errorClass":"CopyNotAllowedError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/from_dataframe.py","lineNumber":238,"sourceCode":"\ndef _construct_offsets_buffer(\n    buffer: Buffer,\n    dtype: Dtype,\n    offset: int,\n    *,\n    allow_copy: bool,\n) -> Series:\n    polars_dtype = dtype_to_polars_dtype(dtype)\n    length = get_buffer_length_in_elements(buffer.bufsize, dtype) - offset\n\n    buffer_info = (buffer.ptr, offset, length)\n    s = pl.Series._from_buffer(polars_dtype, buffer_info, owner=buffer)\n\n    # Polars only supports Int64 offsets\n    if polars_dtype != Int64:\n        if not allow_copy:\n            msg = f\"offsets buffer must be cast from {polars_dtype} to Int64\"\n            raise CopyNotAllowedError(msg)\n        s = s.cast(Int64)\n\n    return s\n\n\ndef _construct_validity_buffer(\n    validity_buffer_info: tuple[Buffer, Dtype] | None,\n    column: Column,\n    column_dtype: PolarsDataType,\n    data: Series,\n    offset: int = 0,\n    *,\n    allow_copy: bool,\n) -> Series | None:\n    null_type, null_value = column.describe_null\n    if null_type == ColumnNullType.NON_NULLABLE or column.null_count == 0:\n        return None\n","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/interchange/from_dataframe.py#L220-L256","documentation":"Polars string offsets are Int64. If a producer provides string offsets in another integer width (Int32 is common in Arrow-style producers), Polars reads the buffer with the producer's dtype and casts it to Int64, which allocates. Under allow_copy=False this raises CopyNotAllowedError.","triggerScenarios":"pl.from_dataframe(df, allow_copy=False) on a String column whose offsets buffer dtype is not Int64 (e.g. Int32).","commonSituations":"Zero-copy consumers receiving Arrow-style Int32 offsets; producer version changes that switch offsets width; combined with error 301, string columns are the least zero-copy-friendly type in the protocol path.","solutions":["Use allow_copy=True for string data","Have the producer widen offsets buffers to Int64","Catch CopyNotAllowedError and retry with allow_copy=True"],"exampleFix":"// before\ndf = pl.from_dataframe(producer_df, allow_copy=False)  # Int32 offsets -> error\n\n// after\ntry:\n    df = pl.from_dataframe(producer_df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    df = pl.from_dataframe(producer_df, allow_copy=True)","handlingStrategy":"try-catch","validationCode":"def string_offsets_are_int64(df) -> bool:\n    from polars.interchange.protocol import DtypeKind\n    proto = df.__dataframe__(allow_copy=False)\n    for col in proto.get_columns():\n        if col.dtype[0] == DtypeKind.STRING and col.size() > 0:\n            _, off_dtype = col.get_buffers()['offsets']\n            if not (off_dtype[1] == 64 and off_dtype[2] == 'l'):\n                return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    out = pl.from_dataframe(df, allow_copy=False)\nexcept pl.exceptions.CopyNotAllowedError:\n    # Int64 offsets cast is unavoidable; allow the copy\n    out = pl.from_dataframe(df, allow_copy=True)","preventionTips":["Producers: widen string offsets buffers to Int64 for polars consumers","Assume any string column forces at least one copy (see also the string repack error) - keep allow_copy=True for string data","Cache producer representation details (offsets width, packing) in integration tests"],"tags":["polars","interchange-protocol","string","zero-copy","memory"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}