mastra-ai/mastra · error

Directory not empty or not found: ${path}

Error message

Directory not empty or not found: ${path}

What it means

`rmdir` on the sandbox filesystem removes a directory. Without `recursive: true` it shells out to the POSIX `rmdir` command, which only succeeds on an empty, existing directory. This error is thrown when the `rmdir` command exits non-zero and the call was not made with `force: true`, covering both the 'directory still has contents' and 'directory does not exist' failure modes (their stderr is collapsed into this single message).

Source

Thrown at mastracode/sdk/src/agents/sandbox-filesystem.ts:418

    const abs = await this.resolveAsync(path);
    await this.assertContainedDest(abs, path);
    const flag = options?.recursive === false ? '' : '-p ';
    await this.execOk(`mkdir ${flag}${shellQuote(abs)}`, `mkdir ${path}`);
  }

  async rmdir(path: string, options?: RemoveOptions): Promise<void> {
    const abs = await this.resolveAsync(path);
    // Same parent containment as deleteFile — `rm -r` through a symlinked
    // parent would otherwise delete outside the workspace.
    await this.assertContainedRealpath(posixPath.dirname(abs), path);
    if (options?.recursive) {
      const force = options?.force ? '-f ' : '';
      await this.execOk(`rm -r ${force}${shellQuote(abs)}`, `rmdir ${path}`);
      return;
    }
    const result = await this.exec(`rmdir ${shellQuote(abs)}`);
    if (result.exitCode !== 0 && !options?.force) {
      throw new Error(`Directory not empty or not found: ${path}`);
    }
  }

  async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {
    const abs = await this.resolveAsync(path);
    await this.assertContainedRealpath(abs, path);
    if (options?.recursive) {
      // Recursive listing emitting "type\tpath". `find -printf` is GNU-only
      // (fails on macOS/BSD hosts backing a local sandbox), so classify each
      // entry with a portable shell loop instead.
      const result = await this.exec(
        `test -d ${shellQuote(abs)} && find ${shellQuote(abs)} -mindepth 1 ${options.maxDepth ? `-maxdepth ${Number(options.maxDepth)} ` : ''}2>/dev/null | while IFS= read -r f; do if [ -d "$f" ]; then printf 'd\\t%s\\n' "$f"; else printf 'f\\t%s\\n' "$f"; fi; done`,
      );
      if (result.exitCode !== 0) throw new Error(`Directory not found: ${path}`);
      return this.parseFindOutput(result.stdout, abs, options);
    }
    // Non-recursive: list with name + type via a portable loop. Use printf,
    // not echo — bash-as-/bin/sh (macOS local sandboxes) does not expand \t

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass `{ recursive: true }` to remove a non-empty directory tree: `fs.rmdir(path, { recursive: true })`.
  2. Pass `{ force: true }` if you want best-effort removal and don't care about failure (e.g. cleanup on shutdown).
  3. Verify the path exists and is a directory with `fs.stat(path)` or `fs.readdir(path)` before calling rmdir.
  4. Empty the directory first (iterate `readdir` and `deleteFile` each entry) if you intentionally want non-recursive removal but only when empty.
  5. Check for typos or stale references: the path may point outside the sandbox containment root or have been deleted already.

Example fix

// before
await sandboxFs.rmdir('./build/cache'); // throws if cache has files
// after
await sandboxFs.rmdir('./build/cache', { recursive: true, force: true });
Defensive patterns

Strategy: try-catch

Validate before calling

const stat = await sandboxFs.stat(path).catch(() => null);
const canRmdir = stat?.type === 'directory' && (await sandboxFs.readdir(path)).length === 0;

Try / catch

try {
  await sandboxFs.rmdir(path);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Directory not empty or not found')) {
    await sandboxFs.rmdir(path, { recursive: true, force: true });
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling `sandboxFs.rmdir(path)` (no options) when the target directory contains files or subdirectories; calling it on a path that does not exist; calling it on a path that resolves to a file rather than a directory. With `force: true` the error is suppressed.

Common situations: Trying to clean up a workspace directory that an agent or build step wrote files into; stale cleanup code after a rename that left the old directory populated or already deleted; a typo'd path that never existed; race conditions where another process removed the directory between existence check and rmdir.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/6a7b02a0cd8b821b. Report an issue: GitHub.