How to Configure Custom Shell Command Timeouts in Harvey AI for Long-Running Operations
Set the --shell-timeout CLI flag (default 60s) or pass default_timeout to Sandbox() to control how long shell commands run before automatic termination.
Harvey AI executes user-provided shell commands inside isolated sandbox containers. For operations that exceed typical durations—data processing pipelines, model training steps, or large file transfers—you'll need to adjust the default timeout threshold. This guide explains the layered timeout architecture in the harveyai/harvey-labs repository and shows you exactly how to configure limits at the CLI, sandbox, and per-command levels.
Understanding the Timeout Architecture
Harvey AI implements a three-layer timeout system that guarantees commands cannot hang indefinitely. Understanding these layers helps you choose the right configuration point for your use case.
Layer 1: Container-Side Enforcement with GNU timeout
Every command executed in the sandbox is wrapped with GNU coreutils timeout. In sandbox/sandbox.py, the exec() method constructs a wrapper that sends SIGTERM, waits 2 seconds, then escalates to SIGKILL:
# sandbox/sandbox.py – exec() implementation
wrapped = f"timeout --kill-after=2 {timeout} bash -lc {_shquote(command)}"
Source: sandbox.py#L89-L104
This ensures hard termination at the container level regardless of Python state.
Layer 2: Sandbox Default Timeout
When instantiating a Sandbox, you can set a default_timeout that applies to all exec() calls unless overridden:
# sandbox/sandbox.py – __init__()
self.default_timeout = default_timeout
Source: sandbox.py#L154-L161
The fallback logic in exec() uses this value when no per-call timeout is provided.
Layer 3: CLI and Programmatic Overrides
The top-level harness and convenience classes expose configuration hooks that funnel down to the sandbox's default_timeout.
Configuring Timeouts from the Command Line
The fastest way to configure custom shell command timeouts in Harvey AI is the --shell-timeout flag in harness/run.py:
# harness/run.py – CLI argument definition
parser.add_argument("--shell-timeout", type=int, default=60,
help="Shell command timeout (seconds)")
Source: run.py#L34-L35
The value is passed directly to the sandbox constructor:
sandbox = Sandbox(..., default_timeout=args.shell_timeout)
Source: run.py#L94-L96
Example: 5-Minute Timeout for Data-Room Review Tasks
python -m harvey_labs.harness.run \
--model claude-sonnet-5 \
--task corporate-ma/review-data-room-red-flag-review \
--shell-timeout 300
This applies a 300-second default to every sandbox command the agent invokes during evaluation.
Configuring Timeouts Programmatically
For Python-based workflows, you have two primary interfaces: direct Sandbox usage and the higher-level ToolExecutor.
Direct Sandbox Configuration
Instantiate Sandbox with default_timeout to establish a baseline for the session:
from harvey_labs.sandbox import Sandbox
# 10-minute default for all commands in this sandbox
sb = Sandbox(
documents_dir="docs",
output_dir="output",
workspace_dir="workspace",
default_timeout=600, # seconds
)
sb.start()
# Uses the sandbox default (600s)
result = sb.exec("ls -R")
# Override for a single command
result = sb.exec("sleep 120", timeout=30)
print("Timed out?", result.timed_out) # → True
The exec() method automatically falls back to self.default_timeout when timeout=None (source: sandbox.py#L99-L102).
ToolExecutor Convenience Class
ToolExecutor maps its shell_timeout parameter to the sandbox's default_timeout:
# harness/tools.py – ToolExecutor.__init__()
self.sandbox = Sandbox(..., default_timeout=shell_timeout)
Source: tools.py#L24-L43
from harvey_labs.harness.tools import ToolExecutor
# 2-minute default for all tool calls
with ToolExecutor(
documents_dir="docs",
output_dir="output",
workspace_dir="workspace",
shell_timeout=120,
) as exec:
# Inherits 120s default
exec.sandbox.exec("python heavy_script.py")
# Per-call override to 45 seconds
exec.sandbox.exec("sleep 300", timeout=45)
Timeout Precedence and Override Behavior
When multiple timeout values are present, Harvey AI resolves them in this priority order:
- Per-call
timeout=argument – Highest priority; passed directly totimeoutcommand - Sandbox
default_timeout– Used when per-call is omitted - Built-in 60-second default – Fallback in CLI if
--shell-timeoutnot specified
This hierarchy lets you set conservative defaults and escalate only where needed.
Key Source Files for Timeout Configuration
| File | Purpose | Relevant Lines |
|---|---|---|
sandbox/sandbox.py |
Core exec() wrapper, default_timeout storage |
L89-L104, L154-L161 |
harness/tools.py |
ToolExecutor parameter forwarding |
L24-L43 |
harness/run.py |
CLI --shell-timeout flag handling |
L34-L35, L94-L96 |
Summary
- Container-level guarantee: All commands wrap GNU
timeout --kill-after=2for reliable termination - CLI configuration: Use
--shell-timeoutwithharness/run.pyfor evaluation-wide defaults - Programmatic control: Pass
default_timeouttoSandbox()orshell_timeouttoToolExecutor() - Per-command precision: Override any default with
timeout=insandbox.exec() - Precedence: Per-call > sandbox default > CLI default > 60s built-in
Frequently Asked Questions
What happens when a command exceeds its timeout?
The sandbox sends SIGTERM to the process group, waits 2 seconds, then sends SIGKILL. The exec() result includes a timed_out boolean field indicating whether termination occurred due to timeout.
Can I disable timeouts entirely?
No—Harvey AI requires timeout enforcement for sandbox isolation. To effectively disable limits, set an extremely high value like timeout=86400 (24 hours) at the appropriate configuration layer.
Does the timeout include container startup time?
No. The timeout command wraps only the shell command execution inside the running container. Sandbox initialization via sb.start() occurs before timing begins.
Why does my 30-second timeout seem to take 32 seconds?
The GNU timeout wrapper includes a --kill-after=2 grace period. After the specified duration, SIGTERM fires; if the process hasn't exited within 2 seconds, SIGKILL forces termination.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →