# How to Configure Custom Shell Command Timeouts in Harvey AI for Long-Running Operations

> Learn how to configure custom shell command timeouts in Harvey AI. Use the CLI flag or Sandbox() to manage long-running operations and prevent infinite loops.

- Repository: [Harvey/harvey-labs](https://github.com/harveyai/harvey-labs)
- Tags: how-to-guide
- Published: 2026-08-11

---

**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`](https://github.com/harveyai/harvey-labs/blob/main/sandbox/sandbox.py), the `exec()` method constructs a wrapper that sends `SIGTERM`, waits 2 seconds, then escalates to `SIGKILL`:

```python

# sandbox/sandbox.py – exec() implementation

wrapped = f"timeout --kill-after=2 {timeout} bash -lc {_shquote(command)}"

```

Source: [`sandbox.py#L89-L104`](https://github.com/harveyai/harvey-labs/blob/main/sandbox/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:

```python

# sandbox/sandbox.py – __init__()

self.default_timeout = default_timeout

```

Source: [`sandbox.py#L154-L161`](https://github.com/harveyai/harvey-labs/blob/main/sandbox/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`](https://github.com/harveyai/harvey-labs/blob/main/harness/run.py):

```python

# 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`](https://github.com/harveyai/harvey-labs/blob/main/harness/run.py#L34-L35)

The value is passed directly to the sandbox constructor:

```python
sandbox = Sandbox(..., default_timeout=args.shell_timeout)

```

Source: [`run.py#L94-L96`](https://github.com/harveyai/harvey-labs/blob/main/harness/run.py#L94-L96)

### Example: 5-Minute Timeout for Data-Room Review Tasks

```bash
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:

```python
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`](https://github.com/harveyai/harvey-labs/blob/main/sandbox/sandbox.py#L99-L102)).

### ToolExecutor Convenience Class

`ToolExecutor` maps its `shell_timeout` parameter to the sandbox's `default_timeout`:

```python

# harness/tools.py – ToolExecutor.__init__()

self.sandbox = Sandbox(..., default_timeout=shell_timeout)

```

Source: [`tools.py#L24-L43`](https://github.com/harveyai/harvey-labs/blob/main/harness/tools.py#L24-L43)

```python
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`](https://github.com/harveyai/harvey-labs/blob/main/sandbox/sandbox.py) | Core `exec()` wrapper, `default_timeout` storage | L89-L104, L154-L161 |
| [`harness/tools.py`](https://github.com/harveyai/harvey-labs/blob/main/harness/tools.py) | `ToolExecutor` parameter forwarding | L24-L43 |
| [`harness/run.py`](https://github.com/harveyai/harvey-labs/blob/main/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`](https://github.com/harveyai/harvey-labs/blob/main/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.