{"record":{"id":"42c4d355cff24533","repo":"pola-rs/polars","slug":"copy-not-allowed-cast-from-arr-dtype-to-dtype","errorCode":null,"errorMessage":"copy not allowed: cast from {arr.dtype} to {dtype} prohibited","messagePattern":"copy not allowed: cast from (.+?) to (.+?) prohibited","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/dataframe/frame.py","lineNumber":1030,"sourceCode":"        https://numpy.org/doc/stable/user/basics.interoperability.html#the-array-method\n        \"\"\"\n        if copy is None:\n            writable, allow_copy = False, True\n        elif copy is True:\n            writable, allow_copy = True, True\n        elif copy is False:\n            writable, allow_copy = False, False\n        else:\n            msg = f\"invalid input for `copy`: {copy!r}\"\n            raise TypeError(msg)\n\n        arr = self.to_numpy(writable=writable, allow_copy=allow_copy)\n\n        if dtype is not None and dtype != arr.dtype:\n            if copy is False:\n                # TODO: Only raise when data must be copied\n                msg = f\"copy not allowed: cast from {arr.dtype} to {dtype} prohibited\"\n                raise RuntimeError(msg)\n\n            arr = arr.__array__(dtype)\n\n        return arr\n\n    @deprecated(\n        \"Support for the dataframe interchange protocol is deprecated since version 1.40.0\"\n    )\n    def __dataframe__(\n        self,\n        nan_as_null: bool = False,  # noqa: FBT001\n        allow_copy: bool = True,  # noqa: FBT001\n    ) -> PolarsDataFrame:\n        \"\"\"\n        Convert to a dataframe object implementing the dataframe interchange protocol.\n\n        .. deprecated:: 1.40.0\n            Support for the Dataframe Interchange Protocol is deprecated.","sourceCodeStart":1012,"sourceCodeEnd":1048,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/dataframe/frame.py#L1012-L1048","documentation":"In DataFrame.__array__, when a target dtype was requested that differs from the DataFrame's natural numpy dtype, a cast requires materializing a new array; if the caller set copy=False (allow_copy=False), that cast is prohibited and polars raises RuntimeError rather than silently violating the zero-copy contract. It is a data-integrity guarantee, not a polars bug.","triggerScenarios":"np.array(df, copy=False, dtype='float32') on an integer or float64 DataFrame; np.array(df, copy=False, dtype=np.int32) against UInt64 data; df.__array__(dtype=..., copy=False) with any lossy or width-changing cast.","commonSituations":"Zero-copy pipelines (differential-evolution loops, shared-memory IPC) that request dtype normalization while pinning copy=False; consumers assuming NumPy casts are free; downcasting for memory limits with copy constraints carried over from another library.","solutions":["Drop the dtype argument and cast afterwards only if a copy is acceptable","Relax the constraint: copy=None (default) or copy=True permits the cast","Cast in polars first so the natural numpy dtype already matches: df = df.cast(pl.Float32) then np.array(df, copy=False)","If zero-copy is mandatory, request exactly the DataFrame's native dtype (e.g. df.to_numpy().dtype)"],"exampleFix":"# before\nnp.array(df, copy=False, dtype='float32')\n\n# after\ndf = df.cast(pl.Float32)\narr = np.array(df, copy=False)","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef zero_copy_array(df, dtype=None):\n    if dtype is not None and np.dtype(dtype) != df.to_numpy().dtype:\n        raise ValueError('cast requested under copy=False; cast the frame first')\n    return np.array(df, copy=False)","typeGuard":null,"tryCatchPattern":"try:\n    arr = np.array(df, copy=False, dtype=dtype)\nexcept RuntimeError as e:\n    if 'copy not allowed' in str(e):\n        arr = np.array(df, dtype=dtype)  # accept one copy as fallback\n    else:\n        raise","preventionTips":["Cast in polars first (df.cast(...)) so the requested dtype matches natively","Never pair copy=False with a dtype that differs from df.to_numpy().dtype","Treat RuntimeError 'copy not allowed' as a contract violation to fix upstream, not to silence"],"tags":["numpy-interop","zero-copy","dtype-cast","copy-semantics"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}