Watch filesystem changes·续

const stream = await sandbox.watch("/workspace/src"); const changedFiles = new Set<string>(); let debounceTimeout: ReturnType<typeof setTimeout> | null = null;

for await (const event of parseSSEStream<FileWatchSSEEvent>(stream)) { if (event.type === "event") { changedFiles.add(event.path);

if (debounceTimeout) { clearTimeout(debounceTimeout); }

debounceTimeout = setTimeout(async () => { console.log(Processing ${changedFiles.size} changed files...); for (const filePath of changedFiles) { await processFile(filePath); } changedFiles.clear(); debounceTimeout = null; }, 1000); } }


### Watch with non-recursive mode

Watch only the top level of a directory, without descending into subdirectories:

```js
import { parseSSEStream } from "@cloudflare/sandbox";

// Only watch root-level config files
const stream = await sandbox.watch("/workspace", {
    include: ["package.json", "tsconfig.json", "vite.config.ts"],
    recursive: false,
});

for await (const event of parseSSEStream(stream)) {
    if (event.type === "event") {
        console.log("Configuration changed, rebuilding project...");
        await sandbox.exec("npm run build");
    }
}
import { parseSSEStream } from "@cloudflare/sandbox";
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";

// Only watch root-level config files
const stream = await sandbox.watch("/workspace", {
    include: ["package.json", "tsconfig.json", "vite.config.ts"],
    recursive: false,
});

for await (const event of parseSSEStream&lt;FileWatchSSEEvent>(stream)) {
    if (event.type === "event") {
        console.log("Configuration changed, rebuilding project...");
        await sandbox.exec("npm run build");
    }
}

Stop a watch

The stream ends naturally when the container sleeps or shuts down. There are two ways to stop a watch early:

Use an AbortController

Pass an AbortSignal to parseSSEStream. Aborting the signal cancels the stream reader, which propagates cleanup to the server. This is the recommended approach when you need to cancel the watch from outside the consuming loop:

import { parseSSEStream } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");
const controller = new AbortController();

// Cancel after 60 seconds
setTimeout(() => controller.abort(), 60_000);

for await (const event of parseSSEStream(stream, controller.signal)) {
    if (event.type === "event") {
        console.log(`${event.eventType}: ${event.path}`);
    }
}

console.log("Watch stopped");
import { parseSSEStream } from "@cloudflare/sandbox";
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");
const controller = new AbortController();

// Cancel after 60 seconds
setTimeout(() => controller.abort(), 60_000);

for await (const event of parseSSEStream&lt;FileWatchSSEEvent>(
    stream,
    controller.signal,
)) {
    if (event.type === "event") {
        console.log(`${event.eventType}: ${event.path}`);
    }
}

console.log("Watch stopped");

Break out of the loop

Breaking out of the for await loop also cancels the stream:

import { parseSSEStream } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");
let eventCount = 0;

for await (const event of parseSSEStream(stream)) {
    if (event.type === "event") {
        console.log(`${event.eventType}: ${event.path}`);
        eventCount++;

// Stop after 100 events
        if (eventCount >= 100) {
            break; // Breaking out of the loop cancels the stream
        }
    }
}

console.log("Watch stopped");
import { parseSSEStream } from "@cloudflare/sandbox";
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");
let eventCount = 0;

for await (const event of parseSSEStream&lt;FileWatchSSEEvent>(stream)) {
    if (event.type === "event") {
        console.log(`${event.eventType}: ${event.path}`);
        eventCount++;

// Stop after 100 events
        if (eventCount >= 100) {
            break; // Breaking out of the loop cancels the stream
        }
    }
}

console.log("Watch stopped");

Best practices

Use server-side filtering

Filter with include or exclude patterns rather than filtering events in JavaScript. Server-side filtering happens at the inotify level, which reduces the number of events sent over the network.

Note

include and exclude are mutually exclusive. Use one or the other, not both. If you need to watch specific file types while ignoring certain directories, use include patterns that match the files you want.

import { parseSSEStream } from "@cloudflare/sandbox";

// Efficient: filtering happens at the inotify level
const stream = await sandbox.watch("/workspace/src", {
    include: ["*.ts"],
});

// Less efficient: all events are sent and then filtered in JavaScript
const stream2 = await sandbox.watch("/workspace/src");
for await (const event of parseSSEStream(stream2)) {
    if (event.type === "event") {
        if (!event.path.endsWith(".ts")) continue;
        // Handle event
    }
}
import { parseSSEStream } from "@cloudflare/sandbox";
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";

// Efficient: filtering happens at the inotify level
const stream = await sandbox.watch("/workspace/src", {
    include: ["*.ts"],
});

// Less efficient: all events are sent and then filtered in JavaScript
const stream2 = await sandbox.watch("/workspace/src");
for await (const event of parseSSEStream&lt;FileWatchSSEEvent>(stream2)) {
    if (event.type === "event") {
        if (!event.path.endsWith(".ts")) continue;
        // Handle event
    }
}

Handle errors in event processing

Errors in your event handler do not stop the watch stream. Wrap handler logic in try...catch to prevent unhandled exceptions:

import { parseSSEStream } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");

for await (const event of parseSSEStream(stream)) {
    if (event.type === "event") {
        try {
            await handleFileChange(event.eventType, event.path);
        } catch (error) {
            console.error(
                `Failed to handle ${event.eventType} for ${event.path}:`,
                error,
            );
            // Continue processing events
        }
    }

if (event.type === "error") {
        console.error("Watch error:", event.error);
    }
}
import { parseSSEStream } from "@cloudflare/sandbox";
import type { FileWatchSSEEvent } from "@cloudflare/sandbox";

const stream = await sandbox.watch("/workspace/src");

for await (const event of parseSSEStream&lt;FileWatchSSEEvent>(stream)) {
    if (event.type === "event") {
        try {
            await handleFileChange(event.eventType, event.path);
        } catch (error) {
            console.error(
                `Failed to handle ${event.eventType} for ${event.path}:`,
                error,
            );
            // Continue processing events
        }
    }

if (event.type === "error") {
        console.error("Watch error:", event.error);
    }
}

Ensure directories exist before watching

Watching a non-existent path returns an error. Verify the path exists before starting a watch:

const watchPath = "/workspace/src";
const result = await sandbox.exists(watchPath);

if (!result.exists) {
    await sandbox.mkdir(watchPath, { recursive: true });
}

const stream = await sandbox.watch(watchPath, {
    include: ["*.ts"],
});
const watchPath = "/workspace/src";
const result = await sandbox.exists(watchPath);

if (!result.exists) {
    await sandbox.mkdir(watchPath, { recursive: true });
}

const stream = await sandbox.watch(watchPath, {
    include: ["*.ts"],
});

Troubleshooting

High CPU usage

If watching large directories causes performance issues:

  1. Use specific include patterns instead of watching everything
  2. Exclude large directories like node_modules and dist
  3. Watch specific subdirectories instead of the entire project
  4. Use recursive: false for shallow monitoring

Path not found errors

All paths must exist and resolve to within /workspace. Relative paths are resolved from /workspace.

Container lifecycle

File watchers are automatically stopped when the sandbox sleeps or shuts down. If the sandbox wakes up, you must re-establish watches in your application logic.

Was this helpful?

YesNo

On this page

Watch filesystem changes·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/sandbox/sdk/guides/file-watching/#page","headline":"Watch filesystem changes","description":"Monitor files and directories in real time with the Sandbox SDK 0.x to build development tools and automation workflows.","url":"https://developers.cloudflare.com/sandbox/sdk/guides/file-watching/","inLanguage":"en","image":"https://developers.cloudflare.com/sandbox/sdk/guides/file-watching/og.png?v=d3741a73df2e386a","dateModified":"2026-09-30","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: Clone repositories, manage branches, and automate Git operations with the Sandbox SDK 0.x. title: Work with Git image: https://developers.cloudflare.com/sandbox/sdk/guides/git-workflows/og.png?v=048dfbf4df6a0a20


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/sandbox/llms.txt
Use this file to discover all available pages before exploring further.