---
title: "Read Files"
description: "We've cloned the repo, but we haven't looked inside. In this lesson, we list the cloned directory with `ls`, read the README out of the Sandbox, and handle the case where the file isn't there."
canonical_url: "https://vercel.com/academy/vercel-sandbox/read-files"
md_url: "https://vercel.com/academy/vercel-sandbox/read-files.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-17T12:42:39.630Z"
content_type: "lesson"
course: "vercel-sandbox"
course_title: "Vercel Sandbox"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Read Files

# Read Files from the Sandbox

We cloned a repo. Now we should probably look at what we got.

There are two ways to inspect a cloned project from outside the Sandbox: run a shell command and parse the output, or read files directly with the SDK. We're going to do both, because both come in handy.

## Outcome

List the cloned repo's contents with `ls`, read a file with `sandbox.readFile`, and handle the case where the file you wanted doesn't exist.

## Fast Track

1. Run `ls -la repo` and log the output.
2. Read `repo/README.md` with `sandbox.readFile`.
3. Add a fallback so a missing file doesn't crash the script.

## Hands-on exercise

Extend `src/sandbox-lifecycle.ts` after the clone step:

```ts
import { Sandbox } from '@vercel/sandbox';

const REPO_URL = 'https://github.com/vercel/examples';

async function main() {
  const sandbox = await Sandbox.create();
  console.log(`Sandbox created: ${sandbox.sandboxId}`);

  const clone = await sandbox.runCommand(`git clone ${REPO_URL} repo`);
  if (clone.exitCode !== 0) {
    console.error(`Clone failed: ${clone.stderr}`);
    await sandbox.stop();
    return;
  }

  const ls = await sandbox.runCommand('ls -la repo');
  console.log('--- repo contents ---');
  console.log(ls.stdout);

  let readmePreview = '(no README found)';
  try {
    const readme = await sandbox.readFile('repo/README.md');
    readmePreview = readme.slice(0, 300);
  } catch (error) {
    console.warn('Could not read README.md:', error);
  }

  console.log('--- README preview ---');
  console.log(readmePreview);

  await sandbox.stop();
}

main();
```

Two things to notice. First, we wrapped `readFile` in `try/catch` because not every repo has a README at that path. Some use lowercase `readme.md`, some put docs in a `docs/` folder, some don't have one at all. We catch the error, log a warning, and keep going. The Sandbox itself doesn't care about your missing README.

Second, we bailed early on a failed clone. Continuing past a failed clone means everything after it fails for confusing reasons. Fail fast, fail loud.

\*\*Warning: Troubleshooting: readFile returns empty string\*\*

An empty string usually means the file exists but is empty, not that it's missing. A missing file throws. If you're getting empty output, check the file in `ls` first.

\*\*Note: Troubleshooting: path mismatch\*\*

`readFile('repo/README.md')` is case-sensitive on the Sandbox filesystem. If `ls` shows `Readme.md` or `readme.md`, you'll need to match the case exactly.

## Try It

```bash
pnpm tsx src/sandbox-lifecycle.ts
```

Expected output:

```txt
Sandbox created: sbx_7N2k4A...
--- repo contents ---
total 32
drwxr-xr-x  ... .git
-rw-r--r--  ... README.md
-rw-r--r--  ... package.json
drwxr-xr-x  ... examples
--- README preview ---
# Vercel Examples

This repository contains a set of example projects...
```

If `README.md` doesn't exist in the repo you're testing, you'll see `(no README found)` instead of crashing. That's the point.

## Commit

```bash
git add src/sandbox-lifecycle.ts
git commit -m "feat(sandbox): list directory and read files from sandbox"
```

## Done-When

- [ ] `ls -la repo` returns the cloned directory listing
- [ ] `sandbox.readFile` returns the README contents on a real repo
- [ ] Missing README logs a warning instead of crashing
- [ ] The script still stops the Sandbox at the end

## Solution

```ts title="src/sandbox-lifecycle.ts"
import { Sandbox } from '@vercel/sandbox';

const REPO_URL = 'https://github.com/vercel/examples';

async function main() {
  const sandbox = await Sandbox.create();
  console.log(`Sandbox created: ${sandbox.sandboxId}`);

  const clone = await sandbox.runCommand(`git clone ${REPO_URL} repo`);
  if (clone.exitCode !== 0) {
    console.error(`Clone failed: ${clone.stderr}`);
    await sandbox.stop();
    return;
  }

  const ls = await sandbox.runCommand('ls -la repo');
  console.log('--- repo contents ---');
  console.log(ls.stdout);

  let readmePreview = '(no README found)';
  try {
    const readme = await sandbox.readFile('repo/README.md');
    readmePreview = readme.slice(0, 300);
  } catch (error) {
    console.warn('Could not read README.md:', error);
  }

  console.log('--- README preview ---');
  console.log(readmePreview);

  await sandbox.stop();
}

main();
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
