Files reads and changes files and directories in a running container. It does not start or stop the container.
Each method runs the sandbox-shim helper binary in the container through exec() and waits for it to finish. Paths follow Linux rules, and Files does not restrict which paths a caller can open.
import { Files } from "@cloudflare/sandbox";
import { DurableObject } from "cloudflare:workers";
export class MyContainer extends DurableObject {
files;
constructor(ctx, env) {
super(ctx, env);
if (!ctx.container) {
throw new Error("The container binding is not configured");
}
// ctx.container stays the same object while the Durable Object runs.
this.files = new Files(ctx.container);
}
// The container must already be running.
async readInput() {
await this.files.mkdir("/workspace", { recursive: true });
await this.files.writeFile("/workspace/input.txt", "hello\n");
return this.files.readFile("/workspace/input.txt");
}
}import { Files } from "@cloudflare/sandbox";
import { DurableObject } from "cloudflare:workers";
export class MyContainer extends DurableObject<Env> {
private readonly files: Files;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
if (!ctx.container) {
throw new Error("The container binding is not configured");
}
// ctx.container stays the same object while the Durable Object runs.
this.files = new Files(ctx.container);
}
// The container must already be running.
async readInput(): Promise<Response> {
await this.files.mkdir("/workspace", { recursive: true });
await this.files.writeFile("/workspace/input.txt", "hello\n");
return this.files.readFile("/workspace/input.txt");
}
}For a complete Worker, refer to Move files in and out of a sandbox.
Before you use Files, meet the package requirements: the helper binary in the image, the nodejs_compat flag, and a running container. Files has no other requirements.
new Files(container: Pick<Container, "exec">)container— the container fromthis.ctx.container, or any object with a compatibleexec()method.
- A path is absolute, or relative to the
cwdoption. - A relative path requires
cwd.cwdmust be absolute. - A path cannot be empty or contain a null character.
- An invalid path or
cwdthrowsTypeErrorbefore the helper process starts. - A relative path is joined onto
cwd, and Linux resolves the joined path. An absolute path ignorescwd. Acwdthat does not exist rejects withSandboxFileErrorENOENT. So does a missing directory anywhere in a path. - Paths resolve through symbolic links as they do in Linux, except where a method states otherwise.
Every method accepts FileOperationOptions:
interface FileOperationOptions {
cwd?: string;
user?: string;
signal?: AbortSignal;
}| Option | Type | Description |
|---|---|---|
cwd |
string |
Absolute directory that resolves a relative path. For rename(), it resolves both paths. |
user |
string |
Numeric Linux user and group IDs as uid:gid, such as "1000:1000". Passed to exec(). When omitted, the helper runs as the image user. A user ID alone, or a user or group name, throws TypeError. |
signal |
AbortSignal |
Cancels the operation and kills its helper process. A signal that fires after the operation finishes has no effect, including an AbortSignal.timeout() that outlives the call. The call rejects with the abort reason: AbortError from abort() without a reason, or TimeoutError from AbortSignal.timeout(). Cancellation can leave the partial effects that each method lists. |
mkdir() also accepts recursive. remove() also accepts recursive and force. An option a method does not accept, or an option of the wrong type, throws TypeError before the helper process starts. Of these options, only user and signal reach exec().
readFile(path: string, options?: FileOperationOptions): Promise<Response>Reads the contents of a file as bytes.
- Returns a
Responsewith status200and noContent-Typeheader. The body applies backpressure to the helper process. - Rejects with
SandboxFileErrorwhen the file cannot be opened, such asENOENTfor a missing file orEISDIRfor a directory. - An error after the file opens errors the response body instead of rejecting the call.
Read the Response like any HTTP response. text(), json(), arrayBuffer(), and blob() load the whole file into memory. For a large file or a file of unknown size, stream body instead.
writeFile(
path: string,
content:
| string
| ArrayBuffer
| ArrayBufferView
| Blob
| ReadableStream<Uint8Array>,
options?: FileOperationOptions,
): Promise<void>Creates a file, or truncates an existing file, then streams content into it.
contentis aFileContentvalue, such as astring, aUint8Array, or aReadableStream<Uint8Array>.- The parent directory must exist. A missing parent rejects with
ENOENT. A directory atpathrejects withEISDIR. - The file opens before a
ReadableStreamis consumed. A later failure can leave the file created, truncated, or partially written. - If the
contentstream errors, the call rejects with the error from the stream instead of aSandboxFileError.
rename() replaces an existing destination in one step. To keep other processes from reading a partially written file, write the file to a temporary path and then rename it:
await files.writeFile(`${path}.partial`, content, { cwd });
await files.rename(`${path}.partial`, path, { cwd });stat(path: string, options?: FileOperationOptions): Promise<SandboxFileStat>Returns SandboxFileStat metadata for a path. Follows a symbolic link at the end of the path.
lstat(path: string, options?: FileOperationOptions): Promise<SandboxFileStat>Returns SandboxFileStat metadata for a path without following a symbolic link at the end of the path. For a symbolic link, type is "symlink" and size is the length of the link target.
readDirectory(
path: string,
options?: FileOperationOptions,
): Promise<SandboxDirectoryEntry[]>Returns the immediate entries of a directory as SandboxDirectoryEntry values.
- The list is not sorted. The filesystem decides the order. The list does not include
.or... pathcan be a symbolic link to a directory. The method follows no other symbolic link.- The method does not recurse or return metadata for each entry.
- A file at
pathrejects withENOTDIR. - An entry name that is not valid UTF-8 rejects the whole call with
EILSEQ.
To list a directory tree, walk it with readDirectory(). This walk does not follow symbolic links to directories:
import type { Files } from "@cloudflare/sandbox";
async function* walk(files: Files, directory: string): AsyncGenerator<string> {
for (const entry of await files.readDirectory(directory)) {
const path = `${directory}/${entry.name}`;
yield path;
if (entry.type === "directory") {
yield* walk(files, path);
}
}
}Each call to readDirectory() starts one process in the container. For a large tree, one container.exec(["find", directory]) call is faster. To cancel it, pass signal to exec().
mkdir(path: string, options?: MkdirOptions): Promise<void>
type MkdirOptions = FileOperationOptions & {
recursive?: boolean;
};Creates a directory.
- With
recursive: true, the method creates missing parent directories. Without it, a missing parent rejects withENOENT.recursivedefaults tofalse. - An existing file at
pathrejects withEEXIST. An existing directory atpathalso rejects withEEXIST, unlessrecursiveistrue. - A file in place of a parent directory rejects with
ENOTDIR. - A failed recursive call can leave the parents it created.
rename(
source: string,
destination: string,
options?: FileOperationOptions,
): Promise<void>Renames a file, directory, or symbolic link with Linux rename rules.
- An existing file at
destinationis replaced. - Renaming a directory onto a non-empty directory rejects with
ENOTEMPTY. - Renaming across filesystems rejects with
EXDEV. - The method does not fall back to copying and deleting.
SandboxFileErrorfrom this method sets bothpathanddestination.
remove(path: string, options?: RemoveOptions): Promise<void>
type RemoveOptions = FileOperationOptions & {
recursive?: boolean;
force?: boolean;
};Removes a file or symbolic link. A symbolic link is removed, not its target.
- A directory requires
recursive: true. Without it, a directory rejects withEISDIR, even whenforceistrue. - With
recursive: true, the method removes the directory tree without following symbolic links inside it. - With
force: true, a missing target resolves instead of rejecting withENOENT. - A failed recursive call can leave part of the tree.
type FileContent =
string | ArrayBuffer | ArrayBufferView | Blob | ReadableStream<Uint8Array>;A string is written as UTF-8.
| Field | Type | Description |
|---|---|---|
type |
SandboxFileType |
File type |
size |
bigint |
Size in bytes |
mode |
number |
Linux mode, including the file type bits. A regular file with permissions 644 has mode 0o100644. |
uid |
number |
Owner user ID |
gid |
number |
Owner group ID |
accessedAt |
Date |
Last access time |
modifiedAt |
Date |
Last content modification time |
changedAt |
Date |
Last metadata change time |
JSON.stringify() and Response.json() throw TypeError for a bigint. Convert size with Number() or String() before serializing a SandboxFileStat. bigint and Date values pass through Durable Object RPC unchanged.
| Field | Type | Description |
|---|---|---|
name |
string |
Entry name |
type |
SandboxFileType |
Entry type. A symbolic link has type "symlink". |
type SandboxFileType =
| "file"
| "directory"
| "symlink"
| "blockDevice"
| "characterDevice"
| "fifo"
| "socket";Linux rejected a file operation.
| Field | Type | Description |
|---|---|---|
name |
"SandboxFileError" |
Error name |
code |
string |
Linux error name, such as ENOENT. UNKNOWN when the runtime has no name for the error. |
operation |
string |
Method that failed: readFile, writeFile, stat, lstat, readDirectory, mkdir, rename, or remove |
path |
string |
Path passed to the method. For rename(), the source path. |
destination |
string |
Destination path. Set only by rename(). undefined for every other method. |
detail |
string |
Error description from the helper |
The message has the form <operation> '<path>': <detail>, for example readFile 'notes.txt': No such file or directory (os error 2).
SandboxFileError is not a class. Use SandboxFileError.is(error) instead of instanceof. It recognizes errors thrown in the same Worker and errors returned through Durable Object RPC.
Files could not complete its exchange with the helper binary. A cloudflare/sandbox image tag that does not match the installed package version can cause this error.
| Field | Type | Description |
|---|---|---|
name |
"SandboxProtocolError" |
Error name |
code |
"SANDBOX_PROTOCOL_ERROR" |
Error code |
detail |
string |
Error description |
Use SandboxProtocolError.is(error) to recognize the error.
Files does not wrap errors from the runtime or from its inputs.
| Condition | Error |
|---|---|
Invalid path, cwd, or other option |
TypeError |
| The container is not running | Error from exec() |
| The image does not contain the helper binary | Error from exec() that names /usr/local/bin/sandbox-shim |
signal is aborted |
The abort reason |
The content stream of writeFile() errors |
The error from the stream |