{"record":{"id":"ced195adbdb84e77","repo":"pola-rs/polars","slug":"at-least-one-of-key-max-rows-per-file-appr","errorCode":null,"errorMessage":"at least one of ('key', 'max_rows_per_file', 'approximate_bytes_per_file') must be specified for PartitionBy","messagePattern":"at least one of \\('key', 'max_rows_per_file', 'approximate_bytes_per_file'\\) must be specified for PartitionBy","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/partition.py","lineNumber":111,"sourceCode":"        key: str | Expr | Sequence[str | Expr] | Mapping[str, Expr] | None = None,\n        include_key: bool | None = None,\n        max_rows_per_file: int | None = None,\n        approximate_bytes_per_file: int | Literal[\"auto\"] | None = \"auto\",\n    ) -> None:\n        msg = \"`PartitionBy` functionality is considered unstable\"\n        issue_unstable_warning(msg)\n\n        if (\n            key is None\n            and max_rows_per_file is None\n            and approximate_bytes_per_file == \"auto\"\n        ):\n            msg = (\n                \"at least one of \"\n                \"('key', 'max_rows_per_file', 'approximate_bytes_per_file') \"\n                \"must be specified for PartitionBy\"\n            )\n            raise ValueError(msg)\n\n        if key is None and include_key is not None:\n            msg = \"cannot use 'include_key' without specifying 'key'\"\n            raise ValueError(msg)\n\n        base_path = str(base_path)\n\n        if approximate_bytes_per_file == \"auto\":\n            approximate_bytes_per_file = (\n                4_294_967_295 if max_rows_per_file is None else None\n            )\n\n        if approximate_bytes_per_file is None:\n            approximate_bytes_per_file = (1 << 64) - 1\n\n        self._pl_partition_by = _PartitionByInner(\n            base_path=base_path,\n            file_path_provider=file_path_provider,","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/partition.py#L93-L129","documentation":"ValueError raised by the PartitionBy constructor (py-polars/src/polars/io/partition.py) used with sink_parquet/sink_ipc to write multiple output files. PartitionBy needs at least one slicing criterion — a key to partition on, a row cap per file, or a byte target per file. With key=None, max_rows_per_file=None, and approximate_bytes_per_file left at its 'auto' default, there is no way to decide when to start a new file, so construction fails immediately (this API is also marked unstable).","triggerScenarios":"pl.LazyFrame(...).sink_parquet(pl.PartitionBy('out/')) with no key, no max_rows_per_file, and no approximate_bytes_per_file — i.e. only a base_path.","commonSituations":"Starting from the doc example and deleting the arguments while prototyping; assuming the constructor defaults to partitioning by row count; passing variables for key/max_rows that unexpectedly evaluate to None (e.g. a config key that was never set).","solutions":["Partition by column values: pl.PartitionBy('out/', key='year').","Or cap file size by rows: pl.PartitionBy('out/', max_rows_per_file=1_000_000).","Or cap by estimated bytes: pl.PartitionBy('out/', approximate_bytes_per_file=128_000_000).","If the arguments come from config, assert they are not all empty before building the PartitionBy."],"exampleFix":"# before\npl.LazyFrame({'year': [2026, 2027]}).sink_parquet(pl.PartitionBy('data/'))\n\n# after\npl.LazyFrame({'year': [2026, 2027]}).sink_parquet(pl.PartitionBy('data/', key='year'))","handlingStrategy":"validation","validationCode":"def make_partition_by(base_path, *, key=None, max_rows_per_file=None, approximate_bytes_per_file='auto', **kw):\n    if key is None and max_rows_per_file is None and approximate_bytes_per_file == 'auto':\n        raise ValueError('PartitionBy needs key, max_rows_per_file, or approximate_bytes_per_file')\n    return pl.PartitionBy(base_path, key=key, max_rows_per_file=max_rows_per_file, approximate_bytes_per_file=approximate_bytes_per_file, **kw)","typeGuard":null,"tryCatchPattern":"try:\n    lf.sink_parquet(pl.PartitionBy(out_dir, **partition_cfg))\nexcept ValueError as e:\n    if 'must be specified for PartitionBy' in str(e):\n        lf.sink_parquet(pl.PartitionBy(out_dir, **{**partition_cfg, 'max_rows_per_file': 1_000_000}))\n    else:\n        raise","preventionTips":["Always pass at least one of key / max_rows_per_file / approximate_bytes_per_file when constructing pl.PartitionBy.","Validate partitioning config objects before submit/sink time in pipeline entry points.","Track that this API is unstable — pin your polars version and re-test partitioning code on upgrades."],"tags":["parquet","sink","partitioning","write","parameter-validation"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}