Problem
LocalPythonExecutor exposes a timeout feature, but a timed-out call can still block until the underlying work finishes.
The current implementation uses future.result(timeout=...) inside a with ThreadPoolExecutor(...) block. When the timeout is hit, control leaves the with block and executor shutdown waits for the worker to finish, so callers do not regain control near the configured timeout boundary.
This is not a security-boundary claim: I understand from the docs that LocalPythonExecutor is best-effort only and not a sandbox. But it does look like a correctness / availability bug, because PR #1910 introduced a "robust timeout mechanism" and the current behavior does not match that expectation.
Steps to reproduce
Please provide a minimal, self-contained, and reproducible example of the bug.
import time
from smolagents.local_python_executor import evaluate_python_code, ExecutionTimeoutError
code = """
import time
time.sleep(3)
result = 42
"""
start = time.monotonic()
try:
evaluate_python_code(code, authorized_imports=["time"], timeout_seconds=1)
except ExecutionTimeoutError as e:
print(type(e).__name__)
print(f"elapsed={time.monotonic() - start:.2f}s")
Actual behavior and error logs
On the current main checkout, I consistently see the timeout exception only after the 3-second sleep finishes:
ExecutionTimeoutError
elapsed=3.00s
I reproduced the same late return through both:
evaluate_python_code(..., timeout_seconds=1)
LocalPythonExecutor(..., timeout_seconds=1)
The root cause appears to be the executor shutdown path waiting for the worker thread after the timeout has already been raised.
Expected behavior
The caller should regain control near the configured timeout boundary (for example, around 1 second in the repro above), even if the background worker cannot be force-killed.
In other words:
- it is fine if the worker thread continues running in the background until completion,
- but the public timeout API should return promptly instead of waiting for the original task duration.
Environment:
Please complete the following information:
- OS: macOS
- Python version: 3.12.12
- Package version: repository checkout at
dc13bbb on main (post-v1.24.0, 2026-04-15)
pytest 9.0.2
smolagents source checkout from main
Additional context (optional)
I searched existing issues/PRs before filing. The closest current item I found is #2108, but that one addresses contextvars / OpenTelemetry propagation in the timeout decorator, not this late-return behavior.
I also validated a minimal local patch that switches timeout cleanup to explicit executor management (shutdown(wait=False, cancel_futures=True) on timeout). With that patch, the same repro returns in about 1.00s, and the targeted timeout tests pass.
Checklist
Problem
LocalPythonExecutorexposes a timeout feature, but a timed-out call can still block until the underlying work finishes.The current implementation uses
future.result(timeout=...)inside awith ThreadPoolExecutor(...)block. When the timeout is hit, control leaves thewithblock and executor shutdown waits for the worker to finish, so callers do not regain control near the configured timeout boundary.This is not a security-boundary claim: I understand from the docs that
LocalPythonExecutoris best-effort only and not a sandbox. But it does look like a correctness / availability bug, because PR #1910 introduced a "robust timeout mechanism" and the current behavior does not match that expectation.Steps to reproduce
Please provide a minimal, self-contained, and reproducible example of the bug.
Actual behavior and error logs
On the current
maincheckout, I consistently see the timeout exception only after the 3-second sleep finishes:I reproduced the same late return through both:
evaluate_python_code(..., timeout_seconds=1)LocalPythonExecutor(..., timeout_seconds=1)The root cause appears to be the executor shutdown path waiting for the worker thread after the timeout has already been raised.
Expected behavior
The caller should regain control near the configured timeout boundary (for example, around 1 second in the repro above), even if the background worker cannot be force-killed.
In other words:
Environment:
Please complete the following information:
dc13bbbonmain(post-v1.24.0, 2026-04-15)Additional context (optional)
I searched existing issues/PRs before filing. The closest current item I found is #2108, but that one addresses
contextvars/ OpenTelemetry propagation in the timeout decorator, not this late-return behavior.I also validated a minimal local patch that switches timeout cleanup to explicit executor management (
shutdown(wait=False, cancel_futures=True)on timeout). With that patch, the same repro returns in about1.00s, and the targeted timeout tests pass.Checklist