pandas-dev/pandas · error · ValueError
expr must be a string to be evaluated, {type(expr)} given
Error message
expr must be a string to be evaluated, {type(expr)} given What it means
pd.eval requires an expression string. A DataFrame or Series passed as expr would otherwise be stringified to its (possibly truncated) repr and then parsed, producing a confusing downstream parse error (GH#16289). pandas short-circuits this with an explicit type guard that names the offending type, so the user sees the real problem instead of a misleading SyntaxError from a truncated repr.
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 71959b8cb9)
Solutions
- Pass a string expression that references the frame's columns, e.g. pd.eval('a + b').
- Operate on the frame directly with vectorized ops (df['a'] + df['b']) or df.eval(...).
- If you have a stringified repr, build the expression from column names, not from the frame object.
Example fix
// before
pd.eval(df)
// after
pd.eval('a + b', local_dict={'a': df['a'], 'b': df['b']}) Defensive patterns
Strategy: type-guard
Validate before calling
def require_str_expr(expr) -> str:
if not isinstance(expr, str):
raise TypeError(
f'expr must be str, got {type(expr).__name__}; '
'pass a column expression string instead'
)
return expr
expr = require_str_expr(expr) Type guard
import pandas as pd
def is_eval_expr_string(expr) -> bool:
return isinstance(expr, str) and not isinstance(expr, (pd.DataFrame, pd.Series)) Try / catch
try:
pd.eval(expr)
except ValueError as e:
if 'must be a string' in str(e):
# operate on the frame directly instead
result = expr # or expr.some_vector_op()
else:
raise Prevention
- Type-check expr at the boundary of any wrapper around pd.eval.
- Keep DataFrame/Series operations on the object API, reserving pd.eval for strings.
- In dynamic pipelines, assert isinstance(expr, str) before pd.eval.
When it happens
Trigger: pd.eval(df), pd.eval(some_series), or any code path that programmatically routes a pandas object into the expr slot of pd.eval instead of a string. Also reachable by feeding eval the result of another computation that returns a frame.
Common situations: Confusing pd.eval with a generic 'evaluate this object' function. Refactoring code where a variable that used to hold a string now holds a DataFrame. Building expression inputs dynamically and forgetting to stringify.
Related errors
- by_row={by_row} not allowed
- Cannot apply ufunc {ufunc} to mixed DataFrame and Series inp
- Column length mismatch: {len(columns)} vs. {K}
- Index length mismatch: {len(index)} vs. {N}
- The '@' prefix is only supported by the pandas parser
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/de107e9f40879483.
Report an issue: GitHub.