# How to Implement Snapshot-Based Sandbox Resume in Open Agents

> Learn how to implement snapshot-based sandbox resume in Open Agents using the Vercel Sandbox SDK to reconnect to stopped VMs efficiently. Save time and resources with this advanced technique.

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

---

**Open Agents implements snapshot-based sandbox resume by storing a persistent VM handle (either `sandboxName` or legacy `snapshotId`) in the session state, then using the Vercel Sandbox SDK's `resume` flag to reconnect to stopped VMs rather than creating fresh instances.**

The vercel-labs/open-agents repository provides a robust implementation of persistent sandbox environments using Vercel's infrastructure. This guide explains how to implement snapshot-based sandbox resume functionality, allowing agent sessions to pause and later reconnect to their exact filesystem state and runtime context.

## Architecture Overview

The resume implementation spans three architectural layers: runtime state utilities, HTTP API endpoints, and SDK integration.

### Runtime State Handling

The [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts) file provides utility functions that manage the durable handle versus volatile runtime state:

- **`hasResumableSandboxState(state)`**: Detects whether a session contains a resumable handle
- **`getResumableSandboxName(state)`**: Extracts the persistent sandbox name for reconnection
- **`clearSandboxState(state)`**: Removes only runtime fields (`expiresAt`, `runtime*`) while preserving the durable handle
- **`clearSandboxResumeState(state)`**: Wipes everything including the resume handle
- **`clearUnavailableSandboxState(state)`**: Handles cases where the sandbox VM no longer exists

### API Surface

The [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) file implements two HTTP endpoints:

**POST /pause**
- Stops the running sandbox via `sandbox.stop()`
- Clears runtime state using `clearSandboxState`
- Returns the saved snapshot ID or `null`

**PUT /resume**
- Validates that a snapshot is available
- If a **named persistent sandbox** exists: calls `connectSandbox(..., { resume: true })`
- If only a **legacy snapshot** exists: creates a new persistent sandbox with the old snapshot ID and `resume: true` (lazy migration)

### Sandbox SDK Integration

The [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) file exposes the `VercelSandbox.connect(name, { resume })` method. This invokes `VercelSandboxSDK.get({ name, resume })`.

When `resume` is `true`, the SDK **re-attaches** to a stopped VM instead of creating a fresh instance, restoring the filesystem and any mounted state.

## Step-by-Step Implementation

### Pausing a Sandbox

When implementing the pause functionality, the system:

1. Receives a `POST` request with `sessionId`
2. Calls `connectSandbox` to create a sandbox instance
3. Invokes `sandbox.stop()` to stop the VM (Vercel automatically creates a snapshot)
4. Strips runtime fields via `clearSandboxState` while keeping `sandboxName` or `snapshotId`
5. Returns the persisted handle to the client

Reference: [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) lines 75-84

### Resuming a Sandbox

The resume process handles both new-style named sandboxes and legacy snapshots:

1. Receives a `PUT` request with `sessionId`
2. Checks for resumable state using `getResumableSandboxName`
3. If a **named sandbox** exists: calls `connectSandbox(..., { resume: true })`
4. If only a **legacy snapshot** exists: creates a new persistent sandbox with the old snapshot ID and `resume: true` (lazy migration)
5. The SDK reconnects to the stopped VM, restoring filesystem and state

Reference: [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) lines 94-119

### SDK Reconnection

The `resume` flag is the critical mechanism:

- `VercelSandbox.connect` invokes `VercelSandboxSDK.get({ name, resume })`
- When `resume` is true, the SDK attaches to the existing stopped VM
- Timeout handling derives from stopped session metadata or uses a default reconnect timeout

Reference: [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) lines 7-25

### State Preservation

The utility functions distinguish between durable and volatile state:

- `clearSandboxState` removes only runtime fields (`expiresAt`, `runtime*`) while preserving durable identifiers such as `sandboxName` or `snapshotId`
- This ensures the VM can be stopped to save costs while maintaining the capability to resume later

Reference: [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts) lines 27-44

## Code Examples

### Client-Side Pause Request

```typescript
// Client side – pause request
await fetch('/api/sandbox/snapshot', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ sessionId: 'abc123' }),
})
  .then(r => r.json())
  .then(data => {
    console.log('Paused, snapshot ID:', data.snapshotId);
  });

```

Implementation: The handler stops the sandbox and clears runtime state while preserving the durable handle. See [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) lines 75-84.

### Client-Side Resume Request

```typescript
// Client side – resume request
await fetch('/api/sandbox/snapshot', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ sessionId: 'abc123' }),
})
  .then(r => r.json())
  .then(data => {
    if (data.success) console.log('Sandbox resumed');
    else console.error('Resume error:', data.error);
  });

```

Implementation: The handler validates available snapshots and calls `connectSandbox(..., { resume: true })` to reconnect. See [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) lines 94-119.

### Programmatic SDK Resume

```typescript
import { connectSandbox } from '@open-harness/sandbox';

// Reconnect to a stopped sandbox by name
const sandbox = await connectSandbox(
  {
    type: 'vercel',
    sandboxName: 'session_abc123', // persisted name
  },
  {
    resume: true,                 // <-- critical flag
    timeout: 15 * 60 * 1000,     // custom timeout if desired
  },
);

```

Implementation: The `resume` option reaches `VercelSandbox.connect`, which forwards it to the SDK. See [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) lines 7-25.

### Checking Resumable State

```typescript
import { getResumableSandboxName, hasResumableSandboxState } from '@/lib/sandbox/utils';

function canResume(state: SandboxState) {
  return hasResumableSandboxState(state);
}

// Example usage in an API route
if (!canResume(sessionRecord.sandboxState)) {
  return Response.json({ error: 'No sandbox available for resume' }, { status: 404 });
}

```

Implementation: Utility functions detect and extract durable handles. See [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts).

## Key Files and Implementation Details

| File | Purpose | Link |
| ---- | ------- | ---- |
| [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts) | Core sandbox class; `connect` method accepts `resume` flag and restores stopped VMs. | https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts |
| [`packages/sandbox/vercel/connect.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/connect.ts) | High-level factory that builds a sandbox config and decides between `connectNamedSandbox` and fresh creation. | https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/connect.ts |
| [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) | HTTP API for pause (`POST`) and resume (`PUT`) – orchestrates state clearing, snapshot ID handling, and lazy migration. | https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts |
| [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts) | State-management helpers: detecting resumable handles, clearing runtime vs. durable state, error classification. | https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts |
| [`packages/sandbox/vercel/snapshot-refresh.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/snapshot-refresh.ts) | Utility used when building a **base** snapshot; not directly part of resume but shows how snapshots are created. | https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/snapshot-refresh.ts |

These files together implement the full **snapshot-based sandbox resume** workflow used throughout Open Agents.

## Summary

- **Persistent handles**: The system stores either `sandboxName` (new style) or `snapshotId` (legacy) in the session record to enable snapshot-based sandbox resume.
- **State separation**: Runtime fields like `expiresAt` are cleared via `clearSandboxState` while durable handles persist, allowing the VM to be stopped without losing the resume capability.
- **SDK resume flag**: Passing `resume: true` to `VercelSandbox.connect` (or `connectSandbox`) triggers the SDK to re-attach to a stopped VM rather than create a fresh instance.
- **Lazy migration**: The resume endpoint handles legacy snapshots by creating new persistent sandboxes with the old snapshot ID and `resume: true`, gradually migrating to the named sandbox model.
- **Error handling**: `clearUnavailableSandboxState` provides graceful fallback when a sandbox VM no longer exists, allowing the system to create a fresh instance instead of failing.

## Frequently Asked Questions

### What is the difference between named sandboxes and legacy snapshots in Open Agents?

Named sandboxes use a persistent `sandboxName` identifier stored in the session state, while legacy snapshots rely on a `snapshotId`. The resume logic in [`apps/web/app/api/sandbox/snapshot/route.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/app/api/sandbox/snapshot/route.ts) handles both: it prioritizes named sandboxes via `getResumableSandboxName`, but can migrate legacy snapshots by creating a new persistent sandbox with the old snapshot ID and `resume: true`.

### How does the `resume` flag work in the Vercel Sandbox SDK?

When `resume: true` is passed to `VercelSandbox.connect` (implemented in [`packages/sandbox/vercel/sandbox.ts`](https://github.com/vercel-labs/open-agents/blob/main/packages/sandbox/vercel/sandbox.ts)), the SDK invokes `VercelSandboxSDK.get({ name, resume })`. This flag instructs the SDK to attach to an existing stopped VM rather than provisioning a new one, restoring the filesystem and runtime state from the last snapshot.

### What happens if a sandbox becomes unavailable after pausing?

The system uses `clearUnavailableSandboxState` from [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts) to detect when a sandbox VM no longer exists. When this occurs, the resume handler clears the stale handle from the session state, allowing the system to fall back to creating a fresh sandbox instead of failing with a connection error.

### How is runtime state cleared while preserving the resume handle?

The `clearSandboxState` utility (in [`apps/web/lib/sandbox/utils.ts`](https://github.com/vercel-labs/open-agents/blob/main/apps/web/lib/sandbox/utils.ts)) selectively removes volatile fields like `expiresAt` and `runtime*` properties while preserving durable identifiers such as `sandboxName` or `snapshotId`. This ensures the VM can be stopped to save costs while maintaining the capability to resume later.