pola-rs/polars · error · TypeError

`replace_with` argument is required if `patterns` argument i

Error message

`replace_with` argument is required if `patterns` argument is not a Mapping type

What it means

Expr.str.replace_many accepts patterns without replace_with only when patterns is a Mapping (dict of pattern to replacement); keys and values are then split internally. With a list, Series, or Expr of patterns the replacements are unknown, so omitting replace_with raises a TypeError immediately.

Source

Thrown at py-polars/src/polars/expr/string.py:2911

        >>> df = pl.DataFrame({"haystack": ["abcd"]})
        >>> patterns = {"abcd": "z", "abc": "y", "b": "x"}
        >>> df.with_columns(
        ...     replaced=pl.col("haystack").str.replace_many(patterns, leftmost=True)
        ... )
        shape: (1, 2)
        ┌──────────┬──────────┐
        │ haystack ┆ replaced │
        │ ---      ┆ ---      │
        │ str      ┆ str      │
        ╞══════════╪══════════╡
        │ abcd     ┆ z        │
        └──────────┴──────────┘
        """  # noqa: W505
        if replace_with is NO_DEFAULT:
            if not isinstance(patterns, Mapping):
                msg = "`replace_with` argument is required if `patterns` argument is not a Mapping type"
                raise TypeError(msg)
            # Early return in case of an empty mapping.
            if not patterns:
                return wrap_expr(self._pyexpr)
            replace_with = list(patterns.values())
            patterns = list(patterns.keys())

        patterns_pyexpr = parse_into_expression(
            patterns,  # type: ignore[arg-type]
            str_as_lit=False,
        )
        replace_with_pyexpr = parse_into_expression(replace_with, str_as_lit=True)
        return wrap_expr(
            self._pyexpr.str_replace_many(
                patterns_pyexpr, replace_with_pyexpr, ascii_case_insensitive, leftmost
            )
        )

    @unstable()

View on GitHub (pinned to df599052da)

Solutions

  1. Pass a dict: .str.replace_many({'foo': 'bar', 'a': 'b'})
  2. Or keep the list and provide replace_with: .str.replace_many(pats, replace_with=repls) — a scalar or list-like of replacements
  3. If patterns/replacements come from a frame, pass expressions: .str.replace_many(pl.col('pats'), replace_with=pl.col('repls'))

Example fix

# before
pl.col('s').str.replace_many(['foo', 'bar'])

# after (mapping)
pl.col('s').str.replace_many({'foo': 'bar', 'bar': 'baz'})

# after (parallel lists)
pl.col('s').str.replace_many(pats, replace_with=repls)
Defensive patterns

Strategy: validation

Validate before calling

from collections.abc import Mapping
if not isinstance(patterns, Mapping) and replace_with is None:
    raise ValueError('replace_with is required for non-mapping patterns')
expr = pl.col('s').str.replace_many(patterns, replace_with=replace_with)

Type guard

from collections.abc import Mapping

def is_pattern_mapping(x: object) -> bool:
    return isinstance(x, Mapping)

Prevention

When it happens

Trigger: .str.replace_many(['foo', 'bar']) with no second argument; passing a Series of patterns positionally; refactoring a dict into two parallel lists and forgetting to add replace_with; wrapper functions that forward patterns but drop the default replace_with.

Common situations: Token replacement tables loaded from CSV/DB as two parallel columns; building patterns dynamically at runtime; migrating from chained .str.replace calls where the replacement was always given.

Related errors


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