{"record":{"id":"b1202cf32b5e6716","repo":"pola-rs/polars","slug":"copy-not-allowed-cast-from-arr-dtype-to-dtype-b1202c","errorCode":null,"errorMessage":"copy not allowed: cast from {arr.dtype} to {dtype} prohibited","messagePattern":"copy not allowed: cast from (.+?) to (.+?) prohibited","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/series/series.py","lineNumber":1614,"sourceCode":"            dtype = np.dtype(\"U\")\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    def __array_ufunc__(\n        self, ufunc: np.ufunc, method: str_, *inputs: Any, **kwargs: Any\n    ) -> Series:\n        \"\"\"Numpy universal functions.\"\"\"\n        if self._s.n_chunks() > 1:\n            self._s.rechunk(in_place=True)\n\n        s = self._s\n\n        if method == \"__call__\":\n            if ufunc.nout != 1:\n                msg = \"only ufuncs that return one 1D array are supported\"\n                raise NotImplementedError(msg)","sourceCodeStart":1596,"sourceCodeEnd":1632,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/series/series.py#L1596-L1632","documentation":"Raised (as RuntimeError, not TypeError) inside Series.__array__ when copy=False was requested but the produced array's dtype differs from the requested dtype, so honoring the request would require a copy. Zero-copy handoff only works when the numpy dtype matches the Series' native representation (e.g. Int64 -> int64, Float64 -> float64, Utf8View -> no match); any cast triggers this.","triggerScenarios":"`np.asarray(s, dtype=np.float32, copy=False)` on an Int64 or Float64 Series; `np.asarray(s, dtype=np.int32, copy=False)` on Int64; requesting U/str dtypes on a String Series together with copy=False; the String->'U' auto-dtype branch only applies when dtype is None.","commonSituations":"NumPy 2 'copy=False means never copy' migrations - old code where copy=False meant 'avoid if possible' now must guarantee exact dtype; performance-tuned pipelines passing copy=False to avoid allocation while still asking for a different precision.","solutions":["Drop copy=False (default allows the copy), or align the requested dtype with the native one: `np.asarray(s, dtype=np.int64, copy=False)` for an Int64 Series.","Make the copy explicit and controlled: `arr = s.to_numpy(); arr32 = arr.astype(np.float32)`.","Cast the Series first so the numpy conversion is already in the target dtype: `s.cast(pl.Float32).to_numpy()`.","Note the TODO in source: the check is coarse - it raises even when a copy might not strictly be needed - so do not rely on copy=False across dtype boundaries."],"exampleFix":"// before\ns = pl.Series([1, 2, 3], dtype=pl.Int64)\nnp.asarray(s, dtype=np.float32, copy=False)  # RuntimeError\n\n// after\ns.cast(pl.Float32).to_numpy()\n# or accept the copy:\nnp.asarray(s, dtype=np.float32)","handlingStrategy":"validation","validationCode":"NATIVE = {pl.Int64: np.int64, pl.Float64: np.float64, pl.Int32: np.int32, pl.Float32: np.float32}\nif np.asarray(s).dtype != target and copy is False:\n    s = s.cast(next(k for k, v in NATIVE.items() if v == target))\narr = np.asarray(s, dtype=target, copy=False)","typeGuard":"def zero_copy_compatible(s: pl.Series, dtype: np.dtype) -> bool:\n    return np.asarray(s).dtype == dtype","tryCatchPattern":"try:\n    arr = np.asarray(s, dtype=target, copy=False)\nexcept RuntimeError as e:\n    if 'copy not allowed' not in str(e):\n        raise\n    arr = np.asarray(s, dtype=target)  # allow the copy","preventionTips":["copy=False is a hard 'never copy' guarantee in NumPy 2 - only pair it with the native dtype.","Cast the Series first (s.cast(pl.Float32).to_numpy()) to control where the copy happens.","Note this raises even for cases where a copy might be avoidable (source TODO)."],"tags":["polars","series","numpy","interop","copy","zero-copy","dtype-cast"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}