Manage files
Read, write, and organize files under /workspace with the typed client and from guest code.
Every sandbox owns a writable /workspace directory, reachable both from guest code and from the caller through a files API. /workspace is shared by every code context in the sandbox regardless of binding, so files written from a Python context are visible from a JavaScript one. This guide shows you how to work with those files from your Worker and from the code you execute. Your own Worker can turn the whole File API off with SANDBOX_FILE_API=disabled (see Environment variables) — everything below assumes it’s enabled, which it is by default.
Use the client’s file methods
import { getSandbox } from "@sandbox-workers/core";
export { Sandbox } from "@sandbox-workers/core";
const sandbox = getSandbox(env.Sandbox, "user-42");
Write a file
await sandbox.writeFile("/workspace/app.py", "print('hi')");
// content is a string, Uint8Array, or ReadableStream<Uint8Array>; pass { encoding: "base64" } for base64 strings
Read a file
const file = await sandbox.readFile("/workspace/app.py");
console.log(file.content);
const stream = await sandbox.readFile("/workspace/image.png", { encoding: "none" });
// stream.content is a ReadableStream<Uint8Array>
List files
const { files } = await sandbox.listFiles("/workspace", { recursive: true });
Create a directory
await sandbox.mkdir("/workspace/project/src", { recursive: true });
Delete a file
await sandbox.deleteFile("/workspace/old.txt", { force: true });
// a directory needs recursive: true; force: true ignores a missing path
Rename or move a file
await sandbox.renameFile("/workspace/draft.md", "/workspace/final.md");
await sandbox.moveFile("/workspace/report.csv", "/workspace/archive/report.csv");
// move additionally requires the destination's parent directory to exist
Check whether a file exists
await sandbox.exists("/workspace/config.json");
See the files API reference for each method’s full signature and return type.
Access files from guest code
JavaScript
fs is a host-backed synchronous subset of Node’s fs:
fs.readFileSync(path, "utf8"); // string; without an encoding, a Uint8Array
fs.writeFileSync(path, data); // string or Uint8Array
fs.readdirSync(path, { withFileTypes });
fs.mkdirSync(path, { recursive });
fs.rmSync(path, { recursive, force });
fs.renameSync(from, to);
fs.existsSync(path);
fs.statSync(path); // { size, mtimeMs, isFile(), isDirectory() }
Errors carry the same Node-style code values as the client’s file methods. import() (and static import inside a module loaded through it) is served from /workspace: ./ and ../ specifiers resolve against the importing module, and only .js/.mjs and .json (with {type: "json"}) files are served — anything outside /workspace fails to resolve.
Python
sys.path includes /workspace, so modules written there can be imported. os.chdir(cwd) changes the working directory used by relative paths; in a code context, the final os.getcwd() is persisted across executions when it is under /workspace.
Perl
chdir($cwd) changes the working directory. In a code context, Cwd::getcwd() is persisted across executions.
Limits
- 1 MiB per file
- 16 MiB per workspace
- 4096 entries
Handle errors
A file operation failure throws the matching SandboxError subclass — FileNotFoundError, FileExistsError, FileTooLargeError, PermissionDeniedError, or FileSystemError — with the path and operation on error.context, and, for filesystem failures, the Node-style errno in error.context.errno.
import { FileNotFoundError } from "@sandbox-workers/core";
try {
await sandbox.readFile("/workspace/missing.txt");
} catch (error) {
if (error instanceof FileNotFoundError) {
// error.code === "FILE_NOT_FOUND", error.httpStatus === 404
}
}
See errors for the full SandboxError hierarchy and error code table.