{"record":{"id":"6a7b02a0cd8b821b","repo":"mastra-ai/mastra","slug":"directory-not-empty-or-not-found-path","errorCode":null,"errorMessage":"Directory not empty or not found: ${path}","messagePattern":"Directory not empty or not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/agents/sandbox-filesystem.ts","lineNumber":418,"sourceCode":"    const abs = await this.resolveAsync(path);\n    await this.assertContainedDest(abs, path);\n    const flag = options?.recursive === false ? '' : '-p ';\n    await this.execOk(`mkdir ${flag}${shellQuote(abs)}`, `mkdir ${path}`);\n  }\n\n  async rmdir(path: string, options?: RemoveOptions): Promise<void> {\n    const abs = await this.resolveAsync(path);\n    // Same parent containment as deleteFile — `rm -r` through a symlinked\n    // parent would otherwise delete outside the workspace.\n    await this.assertContainedRealpath(posixPath.dirname(abs), path);\n    if (options?.recursive) {\n      const force = options?.force ? '-f ' : '';\n      await this.execOk(`rm -r ${force}${shellQuote(abs)}`, `rmdir ${path}`);\n      return;\n    }\n    const result = await this.exec(`rmdir ${shellQuote(abs)}`);\n    if (result.exitCode !== 0 && !options?.force) {\n      throw new Error(`Directory not empty or not found: ${path}`);\n    }\n  }\n\n  async readdir(path: string, options?: ListOptions): Promise<FileEntry[]> {\n    const abs = await this.resolveAsync(path);\n    await this.assertContainedRealpath(abs, path);\n    if (options?.recursive) {\n      // Recursive listing emitting \"type\\tpath\". `find -printf` is GNU-only\n      // (fails on macOS/BSD hosts backing a local sandbox), so classify each\n      // entry with a portable shell loop instead.\n      const result = await this.exec(\n        `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`,\n      );\n      if (result.exitCode !== 0) throw new Error(`Directory not found: ${path}`);\n      return this.parseFindOutput(result.stdout, abs, options);\n    }\n    // Non-recursive: list with name + type via a portable loop. Use printf,\n    // not echo — bash-as-/bin/sh (macOS local sandboxes) does not expand \\t","sourceCodeStart":400,"sourceCodeEnd":436,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/agents/sandbox-filesystem.ts#L400-L436","documentation":"`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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass `{ recursive: true }` to remove a non-empty directory tree: `fs.rmdir(path, { recursive: true })`.","Pass `{ force: true }` if you want best-effort removal and don't care about failure (e.g. cleanup on shutdown).","Verify the path exists and is a directory with `fs.stat(path)` or `fs.readdir(path)` before calling rmdir.","Empty the directory first (iterate `readdir` and `deleteFile` each entry) if you intentionally want non-recursive removal but only when empty.","Check for typos or stale references: the path may point outside the sandbox containment root or have been deleted already."],"exampleFix":"// before\nawait sandboxFs.rmdir('./build/cache'); // throws if cache has files\n// after\nawait sandboxFs.rmdir('./build/cache', { recursive: true, force: true });","handlingStrategy":"try-catch","validationCode":"const stat = await sandboxFs.stat(path).catch(() => null);\nconst canRmdir = stat?.type === 'directory' && (await sandboxFs.readdir(path)).length === 0;","typeGuard":null,"tryCatchPattern":"try {\n  await sandboxFs.rmdir(path);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Directory not empty or not found')) {\n    await sandboxFs.rmdir(path, { recursive: true, force: true });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Default to `{ recursive: true }` unless empty-directory semantics are required","Use `force: true` for idempotent cleanup paths (shutdown, teardown)","`stat` the target to confirm it exists and is a directory before removal","Never assume a directory is empty — agent tools and builds may have written into it"],"tags":["filesystem","sandbox","shell"],"backgroundTag":"directory-not-empty","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}