pola-rs/polars · error · ValueError
cannot specify both `value` and `strategy`
Error message
cannot specify both `value` and `strategy`
What it means
Raised by Expr.fill_null when both a fill `value` and a fill `strategy` are passed. Polars validates arguments in the Python layer before dispatching to the Rust engine: filling is either an explicit literal/expression value or a strategy ('forward', 'backward', 'mean', 'median', 'min', 'max', 'zero', 'one'), never both, because the result would be ambiguous. The check happens eagerly at expression-construction time, before any data is touched.
Source
Thrown at py-polars/src/polars/expr/expr.py:3345
│ 1 ┆ 4.0 │
│ 2 ┆ 5.0 │
│ null ┆ 6.0 │
└──────┴─────┘
>>> df.with_columns(pl.all().fill_null(pl.all().median()))
shape: (3, 2)
┌─────┬─────┐
│ a ┆ b │
│ --- ┆ --- │
│ f64 ┆ f64 │
╞═════╪═════╡
│ 1.0 ┆ 4.0 │
│ 2.0 ┆ 5.0 │
│ 1.5 ┆ 6.0 │
└─────┴─────┘
"""
if value is not None and strategy is not None:
msg = "cannot specify both `value` and `strategy`"
raise ValueError(msg)
elif value is None and strategy is None:
msg = "must specify either a fill `value` or `strategy`"
raise ValueError(msg)
elif strategy not in ("forward", "backward") and limit is not None:
msg = "can only specify `limit` when strategy is set to 'backward' or 'forward'"
raise ValueError(msg)
if value is not None:
value_pyexpr = parse_into_expression(value, str_as_lit=True)
return wrap_expr(self._pyexpr.fill_null(value_pyexpr))
else:
assert strategy is not None
return wrap_expr(self._pyexpr.fill_null_with_strategy(strategy, limit))
def fill_nan(self, value: int | float | Expr | None) -> Expr:
"""
Fill floating point NaN value with a fill value.
View on GitHub (pinned to df599052da)
Solutions
- Remove one of the two arguments: keep `value` for an explicit fill, keep `strategy` for a computed fill.
- If you wanted 'fill with X where possible, else forward-fill', express it explicitly: pl.col('a').fill_null(0) or use coalesce of two fills.
- Audit the call site for kwargs built dynamically (e.g. **fill_kwargs) and make sure only one key survives.
Example fix
# before
pl.col('a').fill_null(0, strategy='forward')
# after
pl.col('a').fill_null(0)
# or
pl.col('a').fill_null(strategy='forward') Defensive patterns
Strategy: validation
Validate before calling
def check_fill_args(value, strategy, limit=None):
if value is not None and strategy is not None:
raise ValueError('pass only one of value / strategy')
if value is None and strategy is None:
raise ValueError('pass at least one of value / strategy')
if limit is not None and strategy not in ('forward', 'backward'):
raise ValueError('limit requires strategy forward/backward')
check_fill_args(0, 'forward') # fails fast before polars sees it Prevention
- Treat fill_null kwargs as mutually exclusive when building dynamic argument dicts.
- Prefer explicit value fills for constants and strategy fills for interpolation; do not combine.
- Fail fast in your own wrapper so the error names your config, not polars internals.
When it happens
Trigger: Calls like pl.col('a').fill_null(0, strategy='forward') or df.fill_null(value=0, strategy='mean'). It fires at expression build time, even inside lazy queries that never run.
Common situations: Refactoring old code that used strategy= and adding a value= default; copy-pasting snippets that merge both styles; config-driven pipelines where a fallback value and a fallback strategy are both populated from settings.
Related errors
- must specify either a fill `value` or `strategy`
- strategy {strategy!r} is not supported
- reinterpret requires exactly one of `signed` or `dtype` to b
- cannot specify both `n` and `fraction`
- `new` argument is required if `old` argument is not a Mappin
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/c8085e9b11d550cf.
Report an issue: GitHub.