huggingface/smolagents · error · InterpreterError
Object is not iterable
Error message
Object is not iterable
What it means
get_iterable is used by the executor's for-loop and comprehension handling to normalize the iterated object: lists pass through, objects with __iter__ are materialized via list(obj), and anything else raises InterpreterError('Object is not iterable'). It mirrors Python's TypeError 'not iterable' inside the sandbox.
Source
Thrown at src/smolagents/local_python_executor.py:329
result = future.result(timeout=timeout_seconds)
return result
except FuturesTimeoutError:
raise ExecutionTimeoutError(
f"Code execution exceeded the maximum execution time of {timeout_seconds} seconds"
)
return wrapper
return decorator
def get_iterable(obj):
if isinstance(obj, list):
return obj
elif hasattr(obj, "__iter__"):
return list(obj)
else:
raise InterpreterError("Object is not iterable")
def fix_final_answer_code(code: str) -> str:
"""
Sometimes an LLM can try to assign a variable to final_answer, which would break the final_answer() tool.
This function fixes this behaviour by replacing variable assignments to final_answer with final_answer_variable,
while preserving function calls to final_answer().
"""
# First, find if there's a direct assignment to final_answer
# Use word boundary and negative lookbehind to ensure it's not an object attribute
assignment_pattern = r"(?<!\.)(?<!\w)\bfinal_answer\s*="
if "final_answer(" not in code or not re.search(assignment_pattern, code):
# If final_answer tool is not called in this blob, then doing the replacement is hazardous because it could false the model's memory for next steps.
# Let's not modify the code and leave the subsequent assignment error happen.
return code
# Pattern for replacing variable assignments
# Looks for 'final_answer' followed by '=' with optional whitespaceView on GitHub (pinned to 30bb116109)
Solutions
- Inspect/guard the value before iterating: `if hasattr(x, '__iter__')` or isinstance checks, and wrap scalars ([x])
- Fix the upstream expression so it produces the intended iterable (e.g. use range(n) instead of n)
- Handle None returns from tools with a default: `items = tool() or []`
Example fix
# before code = "total = 0\nfor x in len([1,2,3]):\n total += x" # after code = "total = 0\nfor x in [1,2,3]:\n total += x"
Defensive patterns
Strategy: type-guard
Validate before calling
def ensure_iterable(v):
if isinstance(v, (list, tuple)):
return list(v)
if hasattr(v, '__iter__'):
return list(v)
return [v] # wrap scalars Type guard
def is_iterable(v) -> bool:
return isinstance(v, (list, tuple, set, dict)) or (hasattr(v, '__iter__') and not isinstance(v, (str, bytes))) or isinstance(v, str) Try / catch
from smolagents.local_python_executor import InterpreterError
try:
evaluate_python(code)
except InterpreterError as e:
if 'not iterable' in str(e):
code = fix_loop_target(code) # wrap the iterated value in [..] Prevention
- Always check a tool's return type before looping over it
- Wrap scalars in a list when the model expects a collection
- Default None-y results to [] with `or []`
When it happens
Trigger: Executed code iterates over an int, float, None, or a non-iterable object, e.g. `for x in 5:` or `for c in len(s):`, or a comprehension like `[i for i in 42]`.
Common situations: LLM assumes a tool returns a list when it returns a scalar or None; iterating a variable before it is assigned; iterating a dict-like result that is actually a JSON number; calling a function that returns None on error and looping over it.
Related errors
- Cannot unpack non-tuple value
- Forbidden access to module: {result.__name__}
- Forbidden access to module: {result['__name__']}
- Forbidden access to function: {function_name}
- Unsupported statement in class body: {stmt.__class__.__name_
AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28).
Data as JSON: /api/errors/89ce0264bfccffba.
Report an issue: GitHub.