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

  1. Switch to df.eval('a = b + 1\nc = a * 2') so the DataFrame is the assignment target.
  2. Pass target=df explicitly: pd.eval(multiline_expr, target=df).
  3. 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

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


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/00e8e0c97a1ac75a. Report an issue: GitHub.