{"record":{"id":"de107e9f40879483","repo":"pandas-dev/pandas","slug":"expr-must-be-a-string-to-be-evaluated-type-expr","errorCode":null,"errorMessage":"expr must be a string to be evaluated, {type(expr)} given","messagePattern":"expr must be a string to be evaluated, (.+?) given","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/computation/eval.py","lineNumber":342,"sourceCode":"    1    pig   20\n\n    We can add a new column using ``pd.eval``:\n\n    >>> pd.eval(\"double_age = df.age * 2\", target=df)\n      animal  age  double_age\n    0    dog   10          20\n    1    pig   20          40\n    \"\"\"\n    inplace = validate_bool_kwarg(inplace, \"inplace\")\n\n    exprs: list[str | BinOp]\n    if isinstance(expr, str):\n        _check_expression(expr)\n        exprs = [e.strip() for e in expr.splitlines() if e.strip() != \"\"]\n    elif isinstance(expr, NDFrame):\n        # GH#16289 a Series/DataFrame would otherwise be converted to its\n        #  (possibly truncated) repr and parsed, producing a confusing error\n        raise ValueError(f\"expr must be a string to be evaluated, {type(expr)} given\")\n    else:\n        # ops.BinOp; for internal compat, not intended to be passed by users\n        exprs = [expr]\n    multi_line = len(exprs) > 1\n\n    if multi_line and target is None:\n        raise ValueError(\n            \"multi-line expressions are only valid in the \"\n            \"context of data, use DataFrame.eval\"\n        )\n    engine = _check_engine(engine)\n    _check_parser(parser)\n    _check_resolvers(resolvers)\n\n    ret = None\n    first_expr = True\n    target_modified = False\n","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/computation/eval.py#L324-L360","documentation":"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.","triggerScenarios":"Calling pd.eval(df) or pd.eval(some_series) where the first positional argument is a DataFrame or Series rather than an expression string.","commonSituations":"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.","solutions":["Pass an expression string, not a DataFrame: pd.eval('col_a + col_b') with the data supplied via target/local_dict/resolvers.","If you meant to evaluate in the context of a DataFrame, use df.eval('col_a + col_b').","Add a type check before the call: assert isinstance(expr, str)."],"exampleFix":"// before\nresult = pd.eval(df)\n\n// after\nresult = df.eval('col_a + col_b')","handlingStrategy":"type-guard","validationCode":"if isinstance(expr, pd.DataFrame) or isinstance(expr, pd.Series):\n    raise TypeError('Pass an expression string, not a DataFrame/Series')\npd.eval(expr)","typeGuard":"import pandas as pd\ndef is_eval_string(e) -> bool:\n    return isinstance(e, str)","tryCatchPattern":"try:\n    pd.eval(expr)\nexcept ValueError as e:\n    if 'expr must be a string' in str(e):\n        # caller passed a DataFrame; redirect to df.eval\n        ...","preventionTips":["Always pass a string literal or string variable as expr.","Use df.eval when operating on a DataFrame.","Add assertions in wrappers to catch type drift."],"tags":["pandas","eval","dataframe","type-error","argument-error"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}