pandas-dev/pandas · error · ValueError

Invalid Attribute context

Error message

Invalid Attribute context {ctx.__name__}

What it means

Raised by PyTablesExprVisitor.visit_Attribute when an AST Attribute node carries a ctx other than ast.Load (e.g. Store or Del). The pytables query visitor only knows how to read attribute values, so a non-Load attribute access is treated as invalid. In practice HDFStore.select(where=...) parses the query in eval mode, which yields Load contexts, so this is an internal guard against malformed/constructed expression trees rather than something users hit through the documented API.

Solutions

  1. Pass the where clause as a plain string (e.g. HDFStore.select(key, where='index >= 5')) so it is parsed in eval mode and attributes get ast.Load context.
  2. If you are building/transforming AST nodes for a pytables query, ensure every ast.Attribute uses ctx=ast.Load() before handing the tree to the visitor.
  3. Avoid attribute-assignment or deletion syntax in a where expression; pytables queries are read-only predicates.

Example fix

// before
node = ast.Attribute(value=ast.Name(id='df', ctx=ast.Store()), attr='A', ctx=ast.Store())
visitor.visit(node)  # raises: Invalid Attribute context Store

// after
node = ast.Attribute(value=ast.Name(id='df', ctx=ast.Load()), attr='A', ctx=ast.Load())
visitor.visit(node)
Defensive patterns

Strategy: validation

Validate before calling

import ast
node = <your attribute ast node>
if not isinstance(getattr(node, 'ctx', None), ast.Load):
    raise ValueError(f'attribute node must use ast.Load ctx, got {type(node.ctx).__name__}')

Type guard

import ast

def is_load_attribute(node) -> bool:
    return isinstance(node, ast.Attribute) and isinstance(node.ctx, ast.Load)

Try / catch

try:
    visitor.visit(attribute_node)
except ValueError as err:
    if 'Invalid Attribute context' in str(err):
        # rewrite the node with ast.Load ctx or fall back to a string query
        ...
    raise

Prevention

When it happens

Trigger: Constructing a PyTablesExprVisitor and feeding it an ast.Attribute node whose .ctx is ast.Store or ast.Del; passing a hand-built AST (not a plain query string) into the pytables expression path; any code path that mutates node.ctx on an attribute before the visitor runs.

Common situations: Almost never seen from the public pandas API because df.query / HDFStore.select parse strings with ast.parse(mode='eval'). May surface in tests, forks, or code that rewrites AST nodes (e.g. swapping ctx) before evaluation.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/e8fe5365b2bba649. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/computation/pytables.py:520

        ctx = type(node.ctx)
        if ctx == ast.Load:
            # resolve the value
            resolved = self.visit(value)

            # try to get the value to see if we are another expression
            try:
                resolved = resolved.value
            except AttributeError:
                pass

            try:
                return self.term_type(getattr(resolved, attr), self.env)
            except AttributeError:
                # something like datetime.datetime where scope is overridden
                if isinstance(value, ast.Name) and value.id == attr:
                    return resolved

        raise ValueError(f"Invalid Attribute context {ctx.__name__}")

    def translate_In(self, op):
        return ast.Eq() if isinstance(op, ast.In) else op

    def _rewrite_membership_op(self, node, left, right):
        return self.visit(node.op), node.op, left, right


def _validate_where(w):
    """
    Validate that the where statement is of the right type.

    The type may either be String, Expr, or list-like of Exprs.

    Parameters
    ----------
    w : String term expression, Expr, or list-like of Exprs.

View on GitHub (pinned to 3b7651241d)