{"record":{"id":"04a0a4b45cfb6ff5","repo":"pola-rs/polars","slug":"n-chunks-must-be-a-multiple-of-the-number-of-chu-04a0a4","errorCode":null,"errorMessage":"`n_chunks` must be a multiple of the number of chunks of this dataframe ({total_n_chunks})","messagePattern":"`n_chunks` must be a multiple of the number of chunks of this dataframe \\((.+?)\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/interchange/dataframe.py","lineNumber":194,"sourceCode":"        -----\n        When the columns in the dataframe are chunked unevenly, or when `n_chunks` is\n        higher than the number of chunks in the dataframe, a slice must be performed\n        that is not on the chunk boundary. This will trigger some compute for columns\n        that contain null values and boolean columns.\n        \"\"\"\n        total_n_chunks = self.num_chunks()\n        chunks = self._get_chunks_from_col_chunks()\n\n        if (n_chunks is None) or (n_chunks == total_n_chunks):\n            for chunk in chunks:\n                yield PolarsDataFrame(chunk, allow_copy=self._allow_copy)\n\n        elif (n_chunks <= 0) or (n_chunks % total_n_chunks != 0):\n            msg = (\n                \"`n_chunks` must be a multiple of the number of chunks of this\"\n                f\" dataframe ({total_n_chunks})\"\n            )\n            raise ValueError(msg)\n\n        else:\n            subchunks_per_chunk = n_chunks // total_n_chunks\n            for chunk in chunks:\n                size = len(chunk)\n                step = size // subchunks_per_chunk\n                if size % subchunks_per_chunk != 0:\n                    step += 1\n                for start in range(0, step * subchunks_per_chunk, step):\n                    yield PolarsDataFrame(\n                        chunk[start : start + step, :],\n                        allow_copy=self._allow_copy,\n                    )\n\n    def _get_chunks_from_col_chunks(self) -> Iterator[DataFrame]:\n        \"\"\"\n        Return chunks of this dataframe according to the chunks of the first column.\n","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/pola-rs/polars/blob/9b5d73fd00236295624374b075d16b1fe6ec6df9/py-polars/src/polars/interchange/dataframe.py#L176-L212","documentation":"Raised by PolarsDataFrame.get_chunks(n_chunks=...) in the dataframe interchange implementation. The method can only split a frame by dividing every existing chunk into an equal number of sub-chunks, so n_chunks must be a positive multiple of the frame's current chunk count (df.num_chunks()); values <= 0, or values that are not exact multiples, raise this ValueError. Passing None or exactly num_chunks() always works because it takes the no-split path.","triggerScenarios":"Calling icdf.get_chunks(n_chunks=k) where the backing pl.DataFrame has total_n_chunks = icdf.num_chunks() chunks and k <= 0 or k % total_n_chunks != 0 (e.g. a 3-chunk frame with n_chunks=2, or n_chunks=0). Interchange consumers such as other libraries calling df.__dataframe__().get_chunks(n_chunks=batch_size) hit this when the source frame is chunked.","commonSituations":"Hardcoding a batch/partition count in generic interop code; receiving a multi-chunk frame from pl.concat, scans, or chunked ingestion and assuming any n_chunks is accepted (any positive value works only when num_chunks() == 1); passing 0 hoping to mean 'default'.","solutions":["Pass n_chunks=None (or exactly icdf.num_chunks()) to iterate the existing chunks unchanged.","Pass a positive multiple of the chunk count, e.g. n_chunks=icdf.num_chunks() * k.","Rechunk the source before interchange: df = df.rechunk() (or pl.concat(..., rechunk=True)) so num_chunks() == 1 and any positive n_chunks is valid."],"exampleFix":"# before\nicdf = df.__dataframe__()\nfor chunk in icdf.get_chunks(n_chunks=4):  # ValueError when df has 3 chunks\n    ...\n\n# after\nicdf = df.rechunk().__dataframe__()  # single chunk -> any positive n_chunks works\nfor chunk in icdf.get_chunks(n_chunks=4):\n    ...","handlingStrategy":"validation","validationCode":"total = icdf.num_chunks()\nif n_chunks is None or n_chunks <= 0 or n_chunks % total != 0:\n    n_chunks = total  # fall back to the frame's natural chunk count\nfor chunk in icdf.get_chunks(n_chunks=n_chunks):\n    ...","typeGuard":null,"tryCatchPattern":"try:\n    chunks = list(icdf.get_chunks(n_chunks=k))\nexcept ValueError as e:\n    if \"`n_chunks` must be a multiple\" not in str(e):\n        raise\n    chunks = list(icdf.get_chunks(n_chunks=icdf.num_chunks()))  # retry with a valid value","preventionTips":["Always derive n_chunks from icdf.num_chunks() instead of hardcoding it.","Call df.rechunk() before exposing a frame to interchange consumers when you control the producer.","Treat n_chunks as 'chunks per existing chunk', not a global batch size; never pass 0 or negatives."],"tags":["polars","interchange-protocol","chunks","n-chunks","valueerror"],"backgroundTag":"dataframe-interchange-protocol","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"}