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:

  1. Per-call timeout= argument – Highest priority; passed directly to timeout command
  2. Sandbox default_timeout – Used when per-call is omitted
  3. Built-in 60-second default – Fallback in CLI if --shell-timeout not 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=2 for reliable termination
  • CLI configuration: Use --shell-timeout with harness/run.py for evaluation-wide defaults
  • Programmatic control: Pass default_timeout to Sandbox() or shell_timeout to ToolExecutor()
  • Per-command precision: Override any default with timeout= in sandbox.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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →