pola-rs/polars · error · NotImplementedError
require dict mapping
Error message
require dict mapping
What it means
When translating a UDF that calls dict.replace_strict (or a mapped replace_strict via a variable looked up in the caller's frame), the parser requires that the referenced variable be a plain dict so it can inline it as a mapping. If the caller-local variable resolved by name is not a dict, NotImplementedError('require dict mapping') is raised.
Solutions
- Make the mapping a plain built-in dict literal or dict variable visible to the UDF's caller scope.
- Use replace (non-strict) or explicit when/then chains instead of replace_strict with a non-dict mapping.
- Perform the replacement outside the UDF using a native expression: pl.col('a').replace_strict(mapping).
Example fix
// before
def fn(v): return str(v).replace_strict(labels)
// after
labels = {'a': 1, 'b': 2} # plain dict in caller scope
def fn(v): return v.replace_strict(labels) Defensive patterns
Strategy: type-guard
Validate before calling
# before using replace_strict inside a UDF, ensure the mapping is a plain dict assert isinstance(labels, dict), 'replace_strict mapping must be a plain dict'
Type guard
def is_plain_dict(v) -> bool:
return type(v) is dict Try / catch
try:
result = df.select(pl.col('a').map_elements(fn))
except NotImplementedError as e:
if 'require dict mapping' in str(e):
result = df.select(pl.col('a').replace_strict(dict(labels))) Prevention
- Use plain dict literals for replace_strict mappings
- Keep mappings in the immediate caller scope of the UDF
- Do the replacement natively: pl.col('a').replace_strict(mapping)
When it happens
Trigger: Inside a map_elements UDF, calling something.replace_strict(x) where x is a local variable in the enclosing function that is not a dict (e.g. a Series, defaultdict-like wrapper, or non-dict mapping); detected via _get_all_caller_variables in _expr.
Common situations: Passing a collections.OrderedDict subclass, a polars Series, or a custom Mapping to replace_strict from within a UDF; defining the mapping in an outer scope the parser cannot resolve as a dict.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- TODO: 'frame' map-function parsing
- TODO: map_target =
- TODO: map target not yet supported.
- accessing ` ` from the top-level `polars` module was…
- altair>=5.4.0 is required for `.plot`
AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18).
Data as JSON: /api/errors/28feab7da5ea7925.
Report an issue: GitHub.
Appendix: source
Thrown at py-polars/src/polars/_utils/udfs.py:725
return f"{op}{e1}"
else:
e2 = self._expr(value.right_operand, col, param_name, depth + 1)
if op in ("is", "is not") and value.left_operand == "None":
not_ = "" if op == "is" else "not_"
return f"{e1}.is_{not_}null()"
elif op in ("in", "not in"):
not_ = "" if op == "in" else "~"
return (
f"{not_}({e1}.is_in({e2}))"
if " " in e1
else f"{not_}{e1}.is_in({e2})"
)
elif op == "replace_strict":
if not self._caller_variables:
self._caller_variables = _get_all_caller_variables()
if not isinstance(self._caller_variables.get(e1, None), dict):
msg = "require dict mapping"
raise NotImplementedError(msg)
return f"{e2}.{op}({e1})"
elif op == "<<":
# 2**e2 may be float if e2 was -ve, but if e1 << e2 was valid then
# e2 must have been +ve. therefore 2**e2 can be safely cast to
# i64, which may be necessary if chaining ops that assume i64.
return f"({e1} * 2**{e2}).cast(pl.Int64)"
elif op == ">>":
# (motivation for the cast is same as the '<<' case above)
return f"({e1} / 2**{e2}).cast(pl.Int64)"
else:
expr = f"{e1} {op} {e2}"
return f"({expr})" if depth else expr
elif value == param_name:
return f'pl.col("{col}")'
return value
View on GitHub (pinned to fe841f959e)