vercel/turborepo · warning

Unable to remove temporary directory ${tempDir}:\n${formatEr

Error message

Unable to remove temporary directory ${tempDir}:\n${formatError(error)}

What it means

After cloning/extracting an example, downloadAndExtractExample's cleanupCloneDirectory (packages/turbo-utils/src/examples.ts:524) tries to remove the temporary clone directory with rmSync (recursive, force, 5 retries, 100ms delay). If removal still throws, it only warns — the example creation itself has already succeeded; the temp directory is left behind.

Source

Thrown at packages/turbo-utils/src/examples.ts:530

function runGit(args: Array<string>, cwd?: string): void {
  try {
    execFileSync("git", args, { cwd, stdio: "pipe" });
  } catch (error) {
    throw new Error(`\`git ${args[0]}\` failed:\n${formatError(error)}`);
  }
}

function cleanupCloneDirectory(tempDir: string): void {
  try {
    rmSync(tempDir, {
      recursive: true,
      force: true,
      maxRetries: 5,
      retryDelay: 100
    });
  } catch (error) {
    warn(
      `Unable to remove temporary directory ${tempDir}:\n${formatError(error)}`
    );
  }
}

export async function downloadAndExtractExample(root: string, name: string) {
  // Validate example name to prevent path traversal and argument injection
  // Only allow alphanumeric characters, hyphens, and underscores
  if (!name || !/^[a-zA-Z0-9_-]+$/.test(name)) {
    throw new Error(`Invalid example name: ${name}`);
  }

  // Normalize and validate the root directory to prevent unsafe git arguments
  const normalizedRoot = resolve(root);
  assertSafeGitArgument(normalizedRoot, "project root");

  const tempDir = join(normalizedRoot, ".turbo-clone-temp");
  assertSafeGitArgument(tempDir, "temporary directory");

View on GitHub (pinned to f9245100cf)

Solutions

  1. Ignore the warning if the example was created correctly — only the temp copy is left behind.
  2. Remove the leftover directory manually once nothing holds locks: rm -rf <tempDir> (path is printed in the warning).
  3. On Windows, close editors/explorer windows and let antivirus finish scanning before retrying deletion.
  4. If it recurs in CI, pre-clean the temp root (set/point TMPDIR to a fresh directory) before running the create command.

Example fix

# before: warning about locked temp dir during `turbo create`
Unable to remove temporary directory /tmp/xyz-clone: EBUSY ...

# after: manually clean up afterwards (safe, example already extracted)
rm -rf /tmp/xyz-clone
Defensive patterns

Strategy: try-catch

Try / catch

// cleanupCloneDirectory already swallows and warns; keep that shape for your own cleanup
import { rmSync } from "node:fs";

try {
  rmSync(tempDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
} catch (error) {
  console.warn(`Unable to remove temporary directory ${tempDir}:\n${formatError(error)}`);
  // example extraction already succeeded — do not rethrow
}

Prevention

When it happens

Trigger: rmSync failing after 5 retries: files in the temp dir are locked by another process (Windows file locking), permission denied on some entries, or the directory is on a filesystem with delayed unlink semantics. Only reached when `turbo create`/example download used a temp clone directory.

Common situations: Windows with antivirus/indexer/Explorer holding handles inside the temp dir, CI containers with odd tmpfs permissions, or disk state where recursive unlink races with file creation. The warning is cosmetic: the downloaded example is already in place.


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/5bc7e807cbb469cb. Report an issue: GitHub.