#2746·smolagents

LocalPythonExecutor: for/while...else silently drops the else clause; list += <non-list iterable> incorrectly rejected

Author: VANDRANKICreated Sep 4, 2026Updated Sep 15, 2026

LocalPythonExecutor (src/smolagents/local_python_executor.py) silently executes for...else and while...else constructs incorrectly: the else clause is never run, and no error is raised.

evaluate_for and evaluate_while read .body, .target, .test off the AST node but never reference .orelse, the field that holds the else: block. The code parses without error (it isn't rejected as an unsupported construct, unlike e.g. match), so the else block is just skipped — a silently wrong result, which is worse than a raised InterpreterError.

Confirmed by running the current main-branch executor:

code = """
result = []
for i in range(3):
    result.append(i)
else:
    result.append(100)
final_answer(result)
"""
  • Real Python: [0, 1, 2, 100]
  • LocalPythonExecutor: [0, 1, 2] — the else branch never executes, no warning.

Same behavior for while...else. Any LLM-generated code action using the for...else/while...else idiom (checking whether a loop completed without break) will silently produce wrong agent state.

Secondary bug found in the same pass, same file: evaluate_augassign's handling of += on a list is stricter than real Python:

if isinstance(expression.op, ast.Add):
    if isinstance(current_value, list):
        if not isinstance(value_to_add, list):
            raise InterpreterError(f"Cannot add non-list value {value_to_add} to a list.")
        current_value += value_to_add

Real Python's list.__iadd__ accepts any iterable via extend semantics (x = [1, 2]; x += (3, 4) gives [1, 2, 3, 4]), but this raises InterpreterError: Cannot add non-list value (3, 4) to a list. for the same code — rejecting valid Python an LLM-authored code action is reasonably likely to produce (tuples, sets, generators, strings).

Both confirmed by executing the unmodified current local_python_executor.py (fetched fresh from main) and comparing against native Python 3.13 output for the same snippets.

Reproduction

from smolagents.local_python_executor import LocalPythonExecutor

executor = LocalPythonExecutor(["*"])

# Bug 1: for-else silently dropped
executor("""
result = []
for i in range(3):
    result.append(i)
else:
    result.append(100)
final_answer(result)
""")
# Returns [0, 1, 2] instead of [0, 1, 2, 100]

# Bug 2: list += non-list iterable incorrectly rejected
executor("""
x = [1, 2]
x += (3, 4)
final_answer(x)
""")
# Raises InterpreterError instead of returning [1, 2, 3, 4]

Fix direction

evaluate_for/evaluate_while should execute node.orelse when the loop completes without hitting a break. evaluate_augassign should accept any iterable for list += , not just list, matching list.__iadd__'s real semantics.