pandas-dev/pandas · error · UndefinedVariableError
name '{key}' is not defined
Error message
name '{key}' is not defined What it means
UndefinedVariableError raised by Scope.resolve() in pandas/core/computation/scope.py:245 with is_local=False/None. It means a bare identifier referenced inside DataFrame.query()/DataFrame.eval()/pd.eval() was not found in any of: the DataFrame columns (resolvers), the calling frame's scope, or the temporaries produced while parsing indexing expressions. The message format 'name {key!r} is not defined' mirrors Python's own NameError phrasing, because UndefinedVariableError subclasses NameError.
Source
Thrown at pandas/core/computation/scope.py:245
if is_local:
return self.scope[key]
# not a local variable so check in resolvers if we have them
if self.has_resolvers:
return self.resolvers[key]
# if we're here that means that we have no locals and we also have
# no resolvers
assert not is_local and not self.has_resolvers
return self.scope[key]
except KeyError:
try:
# last ditch effort we look in temporaries
# these are created when parsing indexing expressions
# e.g., df[df > 0]
return self.temps[key]
except KeyError as err:
raise UndefinedVariableError(key, is_local) from err
def swapkey(self, old_key: str, new_key: str, new_value=None) -> None:
"""
Replace a variable name, with a potentially new value.
Parameters
----------
old_key : str
Current variable name to replace
new_key : str
New variable name to replace `old_key` with
new_value : object
Value to be replaced along with the possible renaming
"""
if self.has_resolvers:
maps = self.resolvers.maps + self.scope.maps
else:
maps = self.scope.mapsView on GitHub (pinned to 71959b8cb9)
Solutions
- Check the identifier against df.columns (e.g. assert 'x' in df.columns) before calling query/eval when the string is dynamic.
- If the name should be a Python variable, prefix it with '@' (df.query('A > @x')); if it should be a column, correct the spelling or add the column.
- When calling pd.eval with explicit namespaces, ensure the name is present in local_dict or global_dict (do not pass empty dicts unless you intend to hide the namespace).
- For dynamic/user-supplied expressions, parse with ast and validate every Name node against an allow-list of columns + known locals before evaluation.
Example fix
// before
df.query("A > x") # UndefinedVariableError: name 'x' is not defined
// after
x = 5
df.query("A > @x") # reference the local explicitly Defensive patterns
Strategy: validation
Validate before calling
import ast
def validate_query_names(expr: str, df, extra_locals: dict | None = None) -> list[str]:
"""Return list of undefined bare names (excluding @locals and callables)."""
tree = ast.parse(expr, mode='eval')
cols = set(df.columns)
known = set((extra_locals or {}).keys())
problems = []
for n in ast.walk(tree):
if isinstance(n, ast.Name) and isinstance(n.ctx, ast.Load):
if n.id not in cols and n.id not in known:
problems.append(n.id)
return problems
# usage
bad = validate_query_names('A > x', df)
assert not bad, f'undefined names: {bad}'
df.query('A > x') Type guard
def names_are_resolvable(expr: str, df, local_ns: dict) -> bool:
import ast
tree = ast.parse(expr, mode='eval')
cols = set(df.columns)
for n in ast.walk(tree):
if isinstance(n, ast.Name):
if n.id not in cols and n.id not in local_ns:
return False
return True Try / catch
import re
from pandas.errors import UndefinedVariableError
try:
result = df.query(expr)
except UndefinedVariableError as e:
# NOTE: UndefinedVariableError does not expose .name/.is_local as attributes;
# the offending identifier only lives in the message text.
m = re.search(r"name '([^']+)' is not defined$", str(e))
bad = m.group(1) if m else str(e)
raise ValueError(f'query references unknown column/variable {bad!r}; columns={list(df.columns)}') from e Prevention
- Treat query/eval strings as code: lint their identifiers against df.columns before running.
- Generate query strings from an allow-list of column names rather than string-interpolating user input.
- When using pd.eval with explicit namespaces, double-check local_dict/global_dict contain every referenced name.
- Remember query does not resolve Python builtins; prefix locals with '@'.
When it happens
Trigger: df.query('A > x') where 'A' is a column but 'x' is neither a column nor a variable in the calling scope; df.eval('col1 + col2') where 'col2' is misspelled or absent; pd.eval('foo + 1', local_dict={}, global_dict={}) with an emptied namespace (GH 47084); referencing a builtin like sin inside query (query does not pick up builtins: df.query('sin > 5')).
Common situations: Renaming a DataFrame column but forgetting to update query/eval strings; dynamic generation of query strings from user input where a column name is missing; passing local_dict/global_dict explicitly to pd.eval and accidentally excluding the needed name; copy-pasted query expressions from a notebook into a function where the referenced local no longer exists; expecting Python builtins (sin, cos, abs) to resolve inside query.
Related errors
- local variable '{key}' is not defined
- Variables in expression "{expr}" overlap with builtins: ({s}
- Invalid engine '{engine}' passed, valid engines are {valid_e
- 'numexpr' is not installed or an unsupported version. Cannot
- expr cannot be an empty string
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/050accb1a98ab6f7.
Report an issue: GitHub.