pandas-dev/pandas · error · ValueError

expr must be a string to be evaluated

Error message

expr must be a string to be evaluated, {type(expr)} given

What it means

Raised in the public eval() function when expr is an instance of NDFrame (a DataFrame or Series) rather than a string. Passing a DataFrame/Series would otherwise be converted to its (possibly truncated) string repr and parsed, producing a confusing downstream error (see GH#16289). This early guard gives a clear message instead.

Solutions

  1. Pass an expression string, not a DataFrame: pd.eval('col_a + col_b') with the data supplied via target/local_dict/resolvers.
  2. If you meant to evaluate in the context of a DataFrame, use df.eval('col_a + col_b').
  3. Add a type check before the call: assert isinstance(expr, str).

Example fix

// before
result = pd.eval(df)

// after
result = df.eval('col_a + col_b')
Defensive patterns

Strategy: type-guard

Validate before calling

if isinstance(expr, pd.DataFrame) or isinstance(expr, pd.Series):
    raise TypeError('Pass an expression string, not a DataFrame/Series')
pd.eval(expr)

Type guard

import pandas as pd
def is_eval_string(e) -> bool:
    return isinstance(e, str)

Try / catch

try:
    pd.eval(expr)
except ValueError as e:
    if 'expr must be a string' in str(e):
        # caller passed a DataFrame; redirect to df.eval
        ...

Prevention

When it happens

Trigger: Calling pd.eval(df) or pd.eval(some_series) where the first positional argument is a DataFrame or Series rather than an expression string.

Common situations: Refactoring code that previously called df.eval and was changed to pd.eval while leaving the DataFrame as the first argument; passing the wrong variable (the data instead of the expression string); programmatic code that selects between an object and a string and accidentally passes the object.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/de107e9f40879483. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/computation/eval.py:342

    1    pig   20

    We can add a new column using ``pd.eval``:

    >>> pd.eval("double_age = df.age * 2", target=df)
      animal  age  double_age
    0    dog   10          20
    1    pig   20          40
    """
    inplace = validate_bool_kwarg(inplace, "inplace")

    exprs: list[str | BinOp]
    if isinstance(expr, str):
        _check_expression(expr)
        exprs = [e.strip() for e in expr.splitlines() if e.strip() != ""]
    elif isinstance(expr, NDFrame):
        # GH#16289 a Series/DataFrame would otherwise be converted to its
        #  (possibly truncated) repr and parsed, producing a confusing error
        raise ValueError(f"expr must be a string to be evaluated, {type(expr)} given")
    else:
        # ops.BinOp; for internal compat, not intended to be passed by users
        exprs = [expr]
    multi_line = len(exprs) > 1

    if multi_line and target is None:
        raise ValueError(
            "multi-line expressions are only valid in the "
            "context of data, use DataFrame.eval"
        )
    engine = _check_engine(engine)
    _check_parser(parser)
    _check_resolvers(resolvers)

    ret = None
    first_expr = True
    target_modified = False

View on GitHub (pinned to 3b7651241d)