{"record":{"id":"25280efd3a8144ec","repo":"pola-rs/polars","slug":"cannot-concat-empty-list","errorCode":null,"errorMessage":"cannot concat empty list","messagePattern":"cannot concat empty list","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/functions/eager.py","lineNumber":220,"sourceCode":"    ╞═════╪══════╪══════╪═════╡\n    │ 1   ┆ null ┆ null ┆ 7   │\n    │ 3   ┆ null ┆ 6    ┆ 8   │\n    └─────┴──────┴──────┴─────┘\n    >>> pl.concat([df_a1, df_a2, df_a3], how=\"align_inner\")\n    shape: (0, 4)\n    ┌─────┬─────┬─────┬─────┐\n    │ id  ┆ x   ┆ y   ┆ z   │\n    │ --- ┆ --- ┆ --- ┆ --- │\n    │ i64 ┆ i64 ┆ i64 ┆ i64 │\n    ╞═════╪═════╪═════╪═════╡\n    └─────┴─────┴─────┴─────┘\n    \"\"\"  # noqa: W505\n    # unpack/standardise (handles generator input)\n    elems: Sequence[PolarsType] = list(items)\n\n    if not elems:\n        msg = \"cannot concat empty list\"\n        raise ValueError(msg)\n\n    if len(elems) == 1 and isinstance(\n        elems[0], (pl.DataFrame, pl.Series, pl.LazyFrame)\n    ):\n        return elems[0]\n\n    if how.startswith(\"align\"):\n        if not is_non_empty_sequence_of(\n            elems, pl.DataFrame\n        ) and not is_non_empty_sequence_of(  # type: ignore[redundant-expr]\n            elems, pl.LazyFrame\n        ):\n            msg = f\"{how!r} strategy is not supported for {qualified_type_name(elems[0])!r}\"\n            raise TypeError(msg)\n\n        # establish common columns, maintaining the order in which they appear\n        all_columns = list(chain.from_iterable(e.collect_schema() for e in elems))\n        key = {v: k for k, v in enumerate(ordered_unique(all_columns))}","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/functions/eager.py#L202-L238","documentation":"pl.concat requires at least one DataFrame, Series, LazyFrame, or Expr; an empty list — or a generator that yields nothing — raises ValueError immediately after the input is materialized. Polars will not invent an output schema for an empty concat, so there is no defined empty result to return.","triggerScenarios":"pl.concat([]); pl.concat(df for df in frames if keep(df)) where the filter removes everything; concat of partitions from a group_by/split that happens to be empty; a loop that appends frames but never runs its body.","commonSituations":"Batch/ETL jobs where all input files were filtered out or missing; empty partitions in parallel processing; optional input lists that resolved to zero items; error-swallowing upstream code that silently produced no frames.","solutions":["Guard before calling: if not frames, return an explicitly-typed empty result (pl.DataFrame(schema=expected_schema))","Treat the empty case as a signal and check upstream why nothing was produced","For partitioned pipelines, skip the concat step for empty partitions"],"exampleFix":"# before\npl.concat(frames)\n\n# after\nif not frames:\n    out = pl.DataFrame(schema=expected_schema)\nelse:\n    out = pl.concat(frames)","handlingStrategy":"validation","validationCode":"frames = list(frames)\nif not frames:\n    out = pl.DataFrame(schema=expected_schema)  # explicit empty result\nelse:\n    out = pl.concat(frames)","typeGuard":null,"tryCatchPattern":"try:\n    out = pl.concat(frames)\nexcept ValueError as e:\n    if 'empty' in str(e):\n        out = pl.DataFrame(schema=expected_schema)\n    else:\n        raise","preventionTips":["Materialize generators with list() before checking emptiness","Log or alert when an input batch is empty — it usually indicates upstream failure","Define the expected empty schema once and reuse it"],"tags":["polars","concat","valueerror","empty-input","etl"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}