pola-rs/polars · error · ValueError

Series only supports 'vertical' concat strategy

Error message

Series only supports 'vertical' concat strategy

What it means

Raised by pl.concat when every element of the input is a pl.Series but `how` is not 'vertical'. A Series is a single 1-D column, so there is no horizontal, diagonal, or aligned layout to concatenate it into. The Python layer validates this before any Rust concat code runs.

Source

Thrown at py-polars/src/polars/functions/eager.py:369

        elif how == "horizontal_extend":
            return wrap_ldf(
                plr.concat_lf_horizontal(
                    elems,
                    parallel=parallel,
                    strict=False,
                )
            )
        else:
            allowed = ", ".join(repr(m) for m in get_args(ConcatMethod))
            msg = f"LazyFrame `how` must be one of {{{allowed}}}, got {how!r}"
            raise ValueError(msg)

    elif is_non_empty_sequence_of(elems, pl.Series):
        if how == "vertical":
            out = wrap_s(plr.concat_series(elems))
        else:
            msg = "Series only supports 'vertical' concat strategy"
            raise ValueError(msg)

    elif is_non_empty_sequence_of(elems, pl.Expr):
        return wrap_expr(plr.concat_expr([e._pyexpr for e in elems], rechunk))
    else:
        msg = f"did not expect type: {qualified_type_name(elems[0])!r} in `concat`"
        raise TypeError(msg)

    if rechunk:
        return out.rechunk()
    return out


def union(
    items: Iterable[PolarsType],
    *,
    how: ConcatMethod = "vertical",
    strict: bool | None = None,
) -> PolarsType:

View on GitHub (pinned to df599052da)

Solutions

  1. Use how='vertical' (the default) for Series: pl.concat([s1, s2])
  2. To place Series side by side as columns, build a DataFrame: pl.DataFrame([s1, s2]) or pl.concat([s1.to_frame(), s2.to_frame()], how='horizontal')
  3. For supertype casting (vertical_relaxed), cast the Series yourself first with s.cast(...) and then concat vertically

Example fix

# before
out = pl.concat([s1, s2], how='horizontal')  # ValueError

# after (side-by-side columns)
out = pl.DataFrame([s1, s2])
# or valid vertical stacking
out = pl.concat([s1, s2])
Defensive patterns

Strategy: validation

Validate before calling

items = list(items)
if all(isinstance(e, pl.Series) for e in items) and how != 'vertical':
    raise ValueError(f'Series inputs only support how=vertical, got {how!r}')
out = pl.concat(items, how=how)

Type guard

def is_series_list(items) -> bool:
    items = list(items)
    return bool(items) and all(isinstance(e, pl.Series) for e in items)

Prevention

When it happens

Trigger: pl.concat([s1, s2], how='horizontal') (or 'diagonal', 'vertical_relaxed', 'align', 'align_full', ...) where all elements are pl.Series. Typically generic helper code forwards the same `how` string regardless of whether it was handed Series or DataFrames.

Common situations: Helper functions that accept 'frames or series' and pass a caller-supplied strategy straight through; refactors that turn a list of one-column DataFrames into a list of Series; copy-pasting a DataFrame concat call onto Series inputs.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/9c31e11494dc1c1a. Report an issue: GitHub.