huggingface/smolagents · error · InterpreterError
Forbidden access to dunder attribute: {expression.attr}
Error message
Forbidden access to dunder attribute: {expression.attr} What it means
While evaluating attribute access (obj.attr), the executor rejects any attribute whose name both starts and ends with '__' (dunder attributes). This blocks sandbox escapes like obj.__class__.__bases__[0].__subclasses__() or func.__globals__ that use dunders to reach privileged machinery.
Source
Thrown at src/smolagents/local_python_executor.py:391
current_node = build_import_tree(authorized_imports)
for part in import_to_check.split("."):
if "*" in current_node:
return True
if part not in current_node:
return False
current_node = current_node[part]
return True
def evaluate_attribute(
expression: ast.Attribute,
state: dict[str, Any],
static_tools: dict[str, Callable],
custom_tools: dict[str, Callable],
authorized_imports: list[str],
) -> Any:
if expression.attr.startswith("__") and expression.attr.endswith("__"):
raise InterpreterError(f"Forbidden access to dunder attribute: {expression.attr}")
value = evaluate_ast(expression.value, state, static_tools, custom_tools, authorized_imports)
return getattr(value, expression.attr)
def evaluate_unaryop(
expression: ast.UnaryOp,
state: dict[str, Any],
static_tools: dict[str, Callable],
custom_tools: dict[str, Callable],
authorized_imports: list[str],
) -> Any:
operand = evaluate_ast(expression.operand, state, static_tools, custom_tools, authorized_imports)
if isinstance(expression.op, ast.USub):
return -operand
elif isinstance(expression.op, ast.UAdd):
return operand
elif isinstance(expression.op, ast.Not):
return not operandView on GitHub (pinned to 30bb116109)
Solutions
- Replace dunder introspection with safe equivalents: type(x) instead of x.__class__, getattr-free public attributes, vars(x) where allowed
- Use isinstance checks for type detection instead of __class__ chains
- If you control the code, remove any __-wrapped attribute access entirely — there is no whitelist option in the executor
Example fix
# before code = "final_answer(item.__class__.__name__)" # after code = "final_answer(type(item).__name__)"
Defensive patterns
Strategy: validation
Validate before calling
import ast
for node in ast.walk(ast.parse(code)):
if isinstance(node, ast.Attribute) and node.attr.startswith('__') and node.attr.endswith('__'):
raise ValueError(f'dunder access not allowed: {node.attr} (line {node.lineno})') Try / catch
from smolagents.local_python_executor import InterpreterError
try:
evaluate_python(code)
except InterpreterError as e:
if 'dunder attribute' in str(e):
code = code.replace('.__class__', ' type(') # rewrite to safe equivalents Prevention
- Use type(x) instead of x.__class__ in generated code
- Avoid __dict__/__globals__/__name__ in snippets destined for the sandbox
- Lint generated code for dunder attribute access before execution
When it happens
Trigger: Generated code accesses a dunder attribute: `x.__class__`, `f.__globals__`, `obj.__dict__`, `instance.__init__`, etc. The check fires before the value is even evaluated.
Common situations: LLM tries introspection to escape the sandbox; benign-but-fancy code uses __class__ to check types or __name__ for logging; serialization helpers reaching for __dict__.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Forbidden call to dunder function: {func.__name__}
- Forbidden access to module: {result.__name__}
- Forbidden access to module: {result['__name__']}
- Forbidden access to function: {function_name}
- Invoking a builtin function that has not been explicitly add
AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28).
Data as JSON: /api/errors/4ff10fcdefeef400.
Report an issue: GitHub.