pola-rs/polars · error · TypeError

Cannot pass a dictionary as a single positional argument.\nI

Error message

Cannot pass a dictionary as a single positional argument.\nIf you merely want the *keys*, use:\n  • df.method(*your_dict.keys())\nIf you need the key value pairs, use one of:\n  • unpack as keywords:    df.method(**your_dict)\n  • build expressions:     df.method(expr.alias(k) for k, expr in your_dict.items())

What it means

polars methods such as select/with_columns accept *exprs and **named_exprs. A dict passed as the single positional argument would be silently iterated as its keys by Python, which is almost never intended, so _parse_inputs_as_iterable (parse/expr.py:244) detects a lone Mapping and raises this TypeError listing the three correct spellings.

Source

Thrown at py-polars/src/polars/_utils/parse/expr.py:244


def _parse_inputs_as_iterable(
    inputs: tuple[Any, ...] | tuple[Iterable[Any]],
) -> Iterable[Any]:
    if not inputs:
        return []

    # Ensures that the outermost element cannot be a Dictionary (as an iterable)
    if len(inputs) == 1 and isinstance(inputs[0], Mapping):
        msg = (
            "Cannot pass a dictionary as a single positional argument.\n"
            "If you merely want the *keys*, use:\n"
            "  • df.method(*your_dict.keys())\n"
            "If you need the key value pairs, use one of:\n"
            "  • unpack as keywords:    df.method(**your_dict)\n"
            "  • build expressions:     df.method(expr.alias(k) for k, expr in your_dict.items())"
        )
        raise TypeError(msg)

    # Treat elements of a single iterable as separate inputs
    if len(inputs) == 1 and _is_iterable(inputs[0]):
        return inputs[0]

    return inputs


def _is_iterable(input: Any) -> bool:
    return isinstance(input, Iterable) and not isinstance(
        input, (str, bytes, pl.Series)
    )


def _parse_named_inputs(
    named_inputs: dict[str, IntoExpr], *, structify: bool = False
) -> Iterable[PyExpr]:
    for name, input in named_inputs.items():

View on GitHub (pinned to df599052da)

Solutions

  1. Unpack as keywords: df.with_columns(**expr_map)
  2. Build aliased expressions: df.select(expr.alias(name) for name, expr in expr_map.items())
  3. If only the keys are wanted: df.select(*expr_map.keys())

Example fix

# before
df.select({"a": pl.col("b")})

# after
df.select(pl.col("b").alias("a"))  # or df.select(**{"a": pl.col("b")})
Defensive patterns

Strategy: validation

Validate before calling

from collections.abc import Mapping

if len(inputs) == 1 and isinstance(inputs[0], Mapping):
    raise TypeError("dict must be unpacked: use **expr_map or aliased expressions")
df.select(*inputs)

Type guard

from collections.abc import Mapping

def is_bare_dict_arg(args: tuple) -> bool:
    return len(args) == 1 and isinstance(args[0], Mapping)

Prevention

When it happens

Trigger: df.select({'a': pl.col('b')}); df.with_columns({'x': pl.col('a') + 1}); dynamically built expr maps forwarded as f(dict) instead of f(**dict).

Common situations: Refactoring dict-based configuration into select; copying pandas assign({...}) style; helper code that forwards a kwargs dict positionally.

Related errors


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