pandas-dev/pandas · error · ValueError
multi-line expressions are only valid in the context of data
Error message
multi-line expressions are only valid in the context of data, use DataFrame.eval
What it means
Multi-line expressions (splitlines yielding more than one non-empty line) only make sense when each line can assign into a target object. The top-level pd.eval default target is None, so there is nowhere to write the assignments; pandas tells you to use DataFrame.eval, which supplies the frame as the target. The check fires at eval.py:348 before any parsing happens.
Source
Thrown at pandas/core/computation/eval.py:349
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
for expr in exprs:
expr = _convert_expression(expr)
_check_for_locals(expr, level, parser)
# get our (possibly passed-in) scope
env = ensure_scope(
level + 1,View on GitHub (pinned to 71959b8cb9)
Solutions
- Switch to df.eval('a = b + 1\nc = a * 2') so the DataFrame is the assignment target.
- Pass target=df explicitly: pd.eval(multiline_expr, target=df).
- Split the multi-line string into separate single-line pd.eval calls.
Example fix
// before
pd.eval('a = b + 1\nc = a * 2')
// after
df.eval('a = b + 1\nc = a * 2') Defensive patterns
Strategy: validation
Validate before calling
def route_multiline(expr: str, target):
lines = [ln for ln in expr.splitlines() if ln.strip()]
if len(lines) > 1 and target is None:
raise ValueError(
'Multi-line expression needs a target; use df.eval(...) instead'
)
# at call site:
if isinstance(target_or_df, pd.DataFrame):
target_or_df.eval(multiline_expr)
else:
route_multiline(multiline_expr, target_or_df) Type guard
def needs_target_for_multiline(expr: str, target) -> bool:
return len([l for l in expr.splitlines() if l.strip()]) > 1 and target is None Try / catch
try:
pd.eval(expr)
except ValueError as e:
if 'multi-line' in str(e) and target is None:
df.eval(expr) # if a df is in scope
else:
raise Prevention
- Use DataFrame.eval for any string containing newlines.
- When building expression strings dynamically, prefer single-line pd.eval calls.
- Document helper functions to make clear whether they expect single- or multi-line input.
When it happens
Trigger: pd.eval('a = b + 1\nc = a * 2') with no target kwarg. Passing a multi-line string to pd.eval expecting column assignments to materialize somewhere.
Common situations: Copying a multi-line DataFrame.eval recipe into a pd.eval call. Building a chain of column computations as one string and routing it through the wrong entry point.
Related errors
- Multi-line expressions are only valid if all expressions con
- Cannot assign expression output to target
- cannot assign without a target object
- Cannot operate inplace if there is no assignment
- Cannot return a copy of the target
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/00e8e0c97a1ac75a.
Report an issue: GitHub.