pandas-dev/pandas · error · UndefinedVariableError

local variable '{key}' is not defined

Error message

local variable '{key}' is not defined

What it means

UndefinedVariableError raised by Scope.resolve() in pandas/core/computation/scope.py:245 with is_local=True. It fires specifically when an identifier prefixed with '@' (the query/eval local-variable sigil) cannot be found in the calling frame's scope (self.scope). The '@name' syntax tells pandas to resolve name as a Python local/global in the caller, not as a DataFrame column; if that name does not exist in the caller, you get this 'local variable ... is not defined' variant.

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.maps

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Define the variable in the same scope where query/eval is called before using '@name'.
  2. If the value is actually a DataFrame column, remove the '@' prefix and reference the column name directly.
  3. For values that cannot live in the caller's frame (generated strings, exec contexts), pass them explicitly via local_dict on pd.eval instead of '@name'.
  4. Avoid '@obj.attr' attribute access after the sigil; bind obj.attr to a plain local first, then use '@localname'.

Example fix

// before
df.query("@threshold > b")  # local variable 'threshold' is not defined

// after
threshold = 10
df.query("@threshold > b")
Defensive patterns

Strategy: validation

Validate before calling

import ast, inspect

def validate_query_locals(expr: str, caller_locals: dict) -> list[str]:
    """Return @-prefixed names in expr that are missing from caller_locals."""
    # query/eval strip the '@' before resolving, so collect Name nodes whose
    # source span was preceded by '@'. Simple heuristic via regex on tokens.
    import re
    at_names = re.findall(r'@(\w+)', expr)
    return [n for n in at_names if n not in caller_locals]

# usage at the call site
bad = validate_query_locals('@a > b > @c', locals())
assert not bad, f'undefined locals: {bad}'

Type guard

def locals_are_defined(expr: str, caller_locals: dict) -> bool:
    import re
    return all(name in caller_locals for name in re.findall(r'@(\w+)', expr))

Try / catch

import re
from pandas.errors import UndefinedVariableError

try:
    result = df.query(expr)
except UndefinedVariableError as e:
    msg = str(e)
    # NOTE: is_local is encoded only in the message prefix, not as an attribute.
    if msg.startswith('local variable '):
        m = re.search(r"local variable '([^']+)' is not defined$", msg)
        missing = m.group(1) if m else msg
        raise NameError(f'missing @local for query: {missing!r}') from e
    raise

Prevention

When it happens

Trigger: df.query('@a > b') where 'a' is not defined in the calling frame; df.query('@c > 0') inside a function where 'c' was never assigned; referencing '@self.foo' incorrectly (the sigil expects a bare name in scope, not an attribute expression); calling query inside exec()/eval() with no real caller frame for pandas to inspect via sys._getframe.

Common situations: Refactoring code and removing a local variable but leaving its '@name' reference in a query string; using '@' in front of something that is actually meant to be a column (drop the '@'); running query strings generated elsewhere where the intended local is not in scope at the call site; notebook cells run out of order so the referenced local was never defined in the current kernel state.

Related errors


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