# How to Handle Sandbox Timeouts and Extend Them in Open Agents

> Learn to handle sandbox timeouts in Open Agents with extendTimeout and onTimeout hooks. Gracefully save state and avoid SDK enforced hard limits for uninterrupted agent execution.

- Repository: [Vercel Labs/open-agents](https://github.com/vercel-labs/open-agents)
- Tags: how-to-guide
- Published: 2026-04-16

---

**To handle sandbox timeouts in Open Agents, use the `extendTimeout` method on the `VercelSandbox` instance before the proactive timeout expires, and register an `onTimeout` hook to gracefully save state before the SDK enforced hard limit.**

Open Agents executes user code inside isolated Vercel Sandboxes—Firecracker micro-VMs managed by the `@vercel/sandbox` SDK. Understanding how to handle sandbox timeouts and extend them in Open Agents is critical for running long-running workflows without unexpected termination. The system implements a dual-layer timeout strategy: a **proactive timeout** tracked internally by the `VercelSandbox` class and a **hard SDK timeout** enforced by Vercel.

## Understanding the Sandbox Timeout Architecture

### Proactive vs. Hard SDK Timeouts

Every sandbox maintains two distinct timeout mechanisms. The proactive timeout (`_timeout`) is calculated when the sandbox is created and stored as an expiration timestamp (`_expiresAt`). This internal timer fires slightly before the SDK's hard limit, giving your application a window to run cleanup logic. The hard SDK timeout is capped at 5 hours (`MAX_SDK_TIMEOUT_MS`) and cannot be exceeded.

### Key Timeout Constants

In [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts), the timeout logic is governed by these constants:

| Constant | Value | Purpose |
|----------|-------|---------|
| `TIMEOUT_BUFFER_MS` | `30_000` | 30-second safety buffer before the SDK's `beforeStop` hook fires. |
| `MAX_SDK_TIMEOUT_MS` | `18_000_000` | Maximum allowed SDK timeout (5 hours). |
| `MAX_PROACTIVE_TIMEOUT_MS` | `MAX_SDK_TIMEOUT_MS - TIMEOUT_BUFFER_MS` | Highest proactive timeout safely requestable (4 hours 59 minutes 30 seconds). |
| `DEFAULT_RECONNECT_TIMEOUT_MS` | `300_000` | Fallback reconnection timeout (5 minutes) when session metadata is unavailable. |

When `VercelSandbox.create` is called, the user-provided timeout is clamped to `MAX_PROACTIVE_TIMEOUT_MS`, and the SDK timeout is calculated by adding the buffer:

```typescript
// From packages/sandbox/vercel/sandbox.ts
const effectiveTimeout = Math.min(timeout, MAX_PROACTIVE_TIMEOUT_MS);
const sdkTimeout = effectiveTimeout + TIMEOUT_BUFFER_MS;

```

## How to Extend Sandbox Timeouts

### Using the extendTimeout Method

The `VercelSandbox` class exposes an async `extendTimeout` method that forwards the request to the underlying SDK session and updates internal state. This is the primary method for handling sandbox timeouts and extending them in Open Agents during long-running operations.

```typescript
// From packages/sandbox/vercel/sandbox.ts (lines 325-363)
async extendTimeout(additionalMs: number): Promise<{ expiresAt: number }> {
  if (this.isStopped) throw new Error("Cannot extend timeout on stopped sandbox");
  if (this._expiresAt === undefined) throw new Error("Timeout tracking not enabled");

  if (typeof this.session.extendTimeout !== "function") {
    throw new Error("extendTimeout is not supported by this version of @vercel/sandbox");
  }

  await this.session.extendTimeout(additionalMs);   // SDK call
  this._expiresAt += additionalMs;                 // update internal state
  this.rescheduleProactiveStop();                  // restart the timer
  // invokes optional onTimeoutExtended hook
  return { expiresAt: this._expiresAt };
}

```

The method performs three critical actions:
1. **Validates state** — Ensures the sandbox is running and timeout tracking is enabled.
2. **Updates the SDK session** — Calls the underlying `@vercel/sandbox` SDK to extend the hard limit.
3. **Reschedules proactive cleanup** — Recalculates `msUntilTimeout` and restarts the internal `timeoutTimer` to fire before the new expiry.

### Timeout Extension Limits

While you can call `extendTimeout` multiple times, the total duration cannot exceed the SDK's hard limit of 5 hours (`MAX_SDK_TIMEOUT_MS`). Each extension request adds time to the existing expiry, not the original creation time. If you attempt to extend a stopped sandbox, the method throws `"Cannot extend timeout on stopped sandbox"`.

## Handling Timeout Events Gracefully

### Registering the onTimeout Hook

To handle sandbox timeouts gracefully in Open Agents, register the `onTimeout` hook in your sandbox configuration. This hook fires when the proactive timer triggers—approximately 30 seconds before the SDK forces termination.

```typescript
const sandbox = await connectVercelSandbox({
  name: "critical-workflow",
  timeout: 10 * 60_000, // 10 minutes
  hooks: {
    onTimeout: async (sb) => {
      console.log("Proactive timeout triggered");
      await sb.snapshot(); // Persist state before forced stop
    },
  },
});

```

The `onTimeout` hook is your opportunity to snapshot state, flush logs, or notify external systems. It does not prevent the sandbox from stopping; it merely provides a cleanup window.

### Reconnecting After Disconnection

If your application disconnects from a sandbox, you can reconnect using `VercelSandbox.connect`. The library attempts to infer the remaining proactive timeout from session metadata using `getRemainingTimeoutFromSession`. If metadata is unavailable, it defaults to `DEFAULT_RECONNECT_TIMEOUT_MS` (5 minutes).

```typescript
const resumed = await connectVercelSandbox({
  sandboxName: "critical-workflow",
  resume: true,
});

```

Once reconnected, you can immediately call `extendTimeout` to push out the expiry if the remaining time is insufficient.

## Practical Implementation Example

Below is a complete pattern for handling sandbox timeouts and extending them in Open Agents during a long-running build process:

```typescript
import { connectVercelSandbox } from "packages/sandbox/vercel";

// 1. Create with initial 10-minute timeout
const sandbox = await connectVercelSandbox({
  name: "build-pipeline",
  timeout: 10 * 60_000,
  hooks: {
    onTimeout: async (sb) => {
      console.log("Sandbox expiring – creating snapshot…");
      await sb.snapshot();
    },
    onTimeoutExtended: async (sb, added) => {
      console.log(`Extended by ${added}ms, new expiry: ${new Date(sb.expiresAt!).toISOString()}`);
    },
  },
});

// 2. Execute long-running command
await sandbox.exec("npm run build:heavy", "/", 15 * 60_000);

// 3. Check remaining time and extend if needed
const remaining = sandbox.expiresAt! - Date.now();
if (remaining < 5 * 60_000) {
  await sandbox.extendTimeout(5 * 60_000); // Add 5 more minutes
}

// 4. Finalize
const { snapshotId } = await sandbox.snapshot();
console.log("Build complete, snapshot saved:", snapshotId);

```

## Summary

- **Open Agents uses Vercel Sandboxes** (Firecracker micro-VMs) with a dual-timeout system: a proactive internal timer and a hard 5-hour SDK limit.
- **`extendTimeout`** is the primary method for extending sandbox lifetimes; it updates both the SDK session and the internal `timeoutTimer` via `rescheduleProactiveStop`.
- **Timeout constants** defined in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) enforce a 30-second safety buffer (`TIMEOUT_BUFFER_MS`) and cap proactive timeouts at ~4 hours 59 minutes (`MAX_PROACTIVE_TIMEOUT_MS`).
- **Hooks** (`onTimeout`, `onTimeoutExtended`) provide graceful handling of expiration events, allowing state snapshots before forced termination.
- **Reconnection** uses `DEFAULT_RECONNECT_TIMEOUT_MS` (5 minutes) as a fallback when session metadata is unavailable, ensuring you can still call `extendTimeout` after reconnecting.

## Frequently Asked Questions

### How long can a sandbox run before it times out?

The maximum runtime is 5 hours (18,000,000ms), enforced by the `@vercel/sandbox` SDK. While you can extend timeouts repeatedly using `extendTimeout`, the cumulative duration cannot exceed this hard limit defined in `MAX_SDK_TIMEOUT_MS`.

### What happens if I don't extend the timeout before it expires?

If you do not call `extendTimeout`, the sandbox enters a timeout phase. First, your `onTimeout` hook fires (if registered), giving you approximately 30 seconds to snapshot state or clean up. Then the SDK hard-stops the Firecracker micro-VM. Once stopped, you cannot extend the timeout and must reconnect or recreate the sandbox.

### Can I extend the timeout after disconnecting and reconnecting?

Yes. When you reconnect using `VercelSandbox.connect`, the library attempts to restore the remaining timeout from session metadata. If unavailable, it defaults to 5 minutes (`DEFAULT_RECONNECT_TIMEOUT_MS`). Once the `VercelSandbox` instance is restored, calling `extendTimeout` works exactly as it does on a fresh sandbox, provided the session has not already stopped.

### What is the TIMEOUT_BUFFER_MS and why is it 30 seconds?

`TIMEOUT_BUFFER_MS` is a 30,000ms (30-second) safety margin defined in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts). It ensures the proactive `onTimeout` hook fires before the SDK's `beforeStop` hook, giving your application a guaranteed window to perform graceful shutdown logic. Without this buffer, the SDK might terminate the VM before your cleanup code executes.